0. Библиотеки и предварительная подготовка

library(readxl)
library(dplyr)
library(scatterPlotMatrix)
library(kableExtra)
library(psych)
library(summarytools)
library(ggplot2)
library(GGally)
library(nortest)
library(moments)
library(ppcor)
library(corrplot)
library(lm.beta)
library(ellipse)
library(car)
library(olsrr)
print_df <- function(df)
{
  df |>
    kable(format = "html") |>
    kable_styling() |>
    kableExtra::scroll_box(width = "100%", height = "100%")
}

plot_qq_graph <- function(data, column_name) {
  data<-data
  expected_quantiles <- qnorm(ppoints(length(data)))
  qqnorm(data,  pch = 19, col = "deeppink")
  qqline(data, distribution = qnorm, lwd = 2, col = "limegreen")
  legend("topleft", legend = column_name, col = "deeppink", pch = 19)
}

plot_pp_plot <- function(data, column_name) {
  n <- length(data)
  expect_prob <- pnorm(data, mean(data), sqrt(var(data) * (n - 1) / n))
  expect_prob <- sort(expect_prob)
  plot(x = expect_prob, y = ppoints(n), col = "red")
  lines(x = c(0, 1), y = c(0, 1), col = "blue")
  legend("topleft", legend = column_name, col = "deeppink", pch = 19)
}

1. Загрузка и предобработка данных

df <- read_excel("stat2021_sm/Sleep/SLEEP_shortname.xls", )
df <- df |> mutate(SLEEP = ifelse(SLEEP < 0, NA, SLEEP), PARADOX = ifelse(is.infinite(PARADOX), NA, PARADOX), PRED_IND = as.factor(PRED_IND), EXP_IND = as.factor(EXP_IND), DANG_IND = as.factor(DANG_IND)) 
df |> slice(1:10) |> print_df()
NAME BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME PRED_IND EXP_IND DANG_IND
African elephant 6654.000 5712.0 NA NA 3.3 38.6 645 3 5 3
African giant rat 1.000 6.6 6.3 2.0 8.3 4.5 42 3 1 3
Arctic Fox 3.385 44.5 NA NA 12.5 14.0 60 1 1 1
Arc. ground squirrel 0.920 5.7 NA NA 16.5 NA 25 5 2 3
Asian elephant 2547.000 4603.0 2.1 1.8 3.9 69.0 624 3 5 4
Baboon 10.550 179.5 9.1 0.7 9.8 27.0 180 4 4 4
Big brown bat 0.023 0.3 15.8 3.9 19.7 19.0 35 1 1 1
Brazilian tapir 160.000 169.0 5.2 1.0 6.2 30.4 392 4 5 4
Cat 3.300 25.6 10.9 3.6 14.5 28.0 63 1 2 1
Chimpanzee 52.160 440.0 8.3 1.4 9.7 50.0 230 1 1 1

2. Описание признаков

  1. NAME — название животного — качественный признак
  2. BODY_WEI — вес тела в килограммах — количественны непрерывный признак
  3. BRAIN_WE — вес мозга в граммах — количественный непрерывный признак
  4. SLOWWAVE — время медленного сна, часов в день — количественный непрерывный признак
  5. PARADOX —время быстрого сна, часов в день — количественный непрерывный признак
  6. SLEEP — общее время сна, часов в день — количественный непрерывный признак
  7. LIFESPAN — продолжительность жизни в годах — количественный непрерывный признак
  8. GESTTIME — время беременности в днях — количественный непрерывный признак
  9. PRED_IND — индекс хищничества (1 — наименее вероятный объект для охоты, 5 — наиболее вероятный объект для охоты) — порядковый признак
  10. EXP_IND — индекс воздействия во время сна (1 — наименее подверженный воздействию (например, животное спит в хорошо защищенном логове), 5 — наиболее подверженный воздействию) — порядковый признак
  11. DANG_IND — общий индекс опасности (на основе двух вышеуказанных индексов и другой информации) (1 — наименьшая опасность (от других животных), 5 — наибольшая опасность (со стороны других животных)) — порядковый признак

3. Матричный график разброса

categories <-list(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1:5, 1:5, 1:5)
df |> dplyr::select(-NAME) |> scatterPlotMatrix(regressionType = 1,
                        corrPlotType = "Text",
                        slidersPosition = list(
                          dimCount = 7,
                          xStartingDimIndex = 1,
                          yStartingDimIndex = 1
                        ),
                        categorical = categories,
                        plotProperties = list(noCatColor = "Indigo"),
                        controlWidgets = TRUE,
                        height = 1050, width = 1000)

Видим достаточно логичные кандидаты на логарифмирование: вес тела, вес мозга, продолжительность жизни и время беременности — это мультиплекативные признаки. Также время быстрого сна имеет перекос влево, так что лучше его тоже отлогарифмировать, хотя логически, так как время сна содержит в себе время быстрого сна, этот признак не является мультиплекативным, а также не имеет нелинейную зависимость. Однако, ассиметрия времи быстрого сна имеет достаточно большое значение:

desc0 <- df |>dplyr::select(-NAME, -PRED_IND, -EXP_IND, -DANG_IND) |> describe() |>dplyr::select(-trimmed, -mad, -se)
print_df(desc0)
vars n mean sd median min max range skew kurtosis
BODY_WEI 1 62 198.789984 899.158011 3.3425 0.005 6654.0 6653.995 6.2494291 40.5981819
BRAIN_WE 2 62 283.134193 930.278942 17.2500 0.140 5712.0 5711.860 4.8288287 23.2367752
SLOWWAVE 3 48 8.672917 3.666452 8.3500 2.100 17.9 15.800 0.2798669 -0.4442636
PARADOX 4 50 1.972000 1.442651 1.8000 0.000 6.6 6.600 1.3667228 1.7793264
SLEEP 5 58 10.532759 4.606760 10.4500 2.600 19.9 17.300 0.1909556 -0.6497094
LIFESPAN 6 58 19.877586 18.206255 15.1000 2.000 100.0 98.000 1.9107968 5.0043782
GESTTIME 7 58 142.353448 146.805039 79.0000 12.000 645.0 633.000 1.5974241 2.3223297

4. Логарифмирование хвостатых

df.log <- df
df.log <- df.log |> mutate(BODY_WEI = log(BODY_WEI), BRAIN_WE = log(BRAIN_WE), PARADOX = log(PARADOX), LIFESPAN = log(LIFESPAN), GESTTIME = log(GESTTIME))
df.log <- df.log |> mutate(PARADOX = ifelse(is.infinite(PARADOX), NA, PARADOX))
df.log |> dplyr::select(-NAME) |> filter(!is.na(SLOWWAVE) & !is.na(PARADOX) & !is.na(SLEEP) & !is.na(LIFESPAN) & !is.na(GESTTIME)) |> scatterPlotMatrix(regressionType = 1,
                        corrPlotType = "Text",
                        slidersPosition = list(
                          dimCount = 7,
                          xStartingDimIndex = 1,
                          yStartingDimIndex = 1
                        ),
                        categorical = categories,
                        plotProperties = list(noCatColor = "Indigo"),
                        controlWidgets = TRUE,
                        height = 1050, width = 1000)

Выведем также немного другой матричный график, в котором удаление NA будет попарным.

df.log |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

Видим, что распределения стали более симметричными, причём почти все признаки имеют отрицательный эксцесс, то есть они более “плоские”, чем нормальное:

desc1 <- df.log |>dplyr::select(-NAME, -PRED_IND, -EXP_IND, -DANG_IND) |> describe() |>dplyr::select(-trimmed, -mad, -se)
print_df(desc1)
vars n mean sd median min max range skew kurtosis
BODY_WEI 1 62 1.3375390 3.1231277 1.2066382 -5.2983174 8.802974 14.101291 0.1453599 -0.5232692
BRAIN_WE 2 62 3.1401979 2.4465120 2.8477071 -1.9661129 8.650324 10.616437 0.0434421 -0.6184785
SLOWWAVE 3 48 8.6729167 3.6664517 8.3500000 2.1000000 17.900000 15.800000 0.2798669 -0.4442636
PARADOX 4 49 0.4656547 0.7080779 0.5877867 -1.2039728 1.887070 3.091042 -0.1364307 -0.5840339
SLEEP 5 58 10.5327586 4.6067604 10.4500000 2.6000000 19.900000 17.300000 0.1909556 -0.6497094
LIFESPAN 6 58 2.5930586 0.9435145 2.7120343 0.6931472 4.605170 3.912023 -0.1629152 -0.9151912
GESTTIME 7 58 4.4441910 1.0602517 4.3596587 2.4849066 6.469250 3.984344 0.0473325 -1.1407247

По графику неравномерностей не видно. Если раскрасить по категориальным признакам, то тоже не получается выделить раздельные облака.

5. Выбросы

Видим пару выбросов, для которых вес мозга мал, но продолжительность жизни достаточно велика:

df.log |> filter(LIFESPAN > 2 & BRAIN_WE < 0) |> print_df()
NAME BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME PRED_IND EXP_IND DANG_IND
Big brown bat -3.772261 -1.203973 15.8 1.3609766 19.7 2.944439 3.555348 1 1 1
Little brown bat -4.605170 -1.386294 17.9 0.6931472 19.9 3.178054 3.912023 1 1 1

Это две летучие мыши.

Построим теперь графики разброса без выбросов.

df.log.out <- df.log |>  filter(!(LIFESPAN > 2 & BRAIN_WE < 0))
df.log.out |> dplyr::select(-NAME) |> filter(!is.na(SLOWWAVE) & !is.na(PARADOX) & !is.na(SLEEP) & !is.na(LIFESPAN) & !is.na(GESTTIME)) |> scatterPlotMatrix(regressionType = 1,
                        corrPlotType = "Text",
                        slidersPosition = list(
                          dimCount = 7,
                          xStartingDimIndex = 1,
                          yStartingDimIndex = 1
                        ),
                        categorical = categories,
                        plotProperties = list(noCatColor = "Indigo"),
                        controlWidgets = TRUE,
                        height = 1050, width = 1000)

И сравним корреляции.

df.log |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

df.log.out |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

В основном они выросли, по крайней мере, там, где мы исключали выбросы.

6. Зависимости от индексов опасности

Необходимо описать разницу между животными по индексу опасности места, где они спят. Посмотрим на количество животных в каждой группе:

df.log.out |>dplyr::select(EXP_IND) |> summary()
 EXP_IND
 1:25   
 2:13   
 3: 4   
 4: 5   
 5:13   

Выборки для индексов \(2\) и \(5\) можем считать сбалансированными.

Визуально оценим распределения с помощью ящиков с усами:

df.log.out |> ggplot(aes(x = EXP_IND, y = BODY_WEI)) +
  geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = BRAIN_WE)) +
  geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = PARADOX)) +
  geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = SLOWWAVE)) +
  geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = SLEEP)) +
  geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = LIFESPAN)) +
  geom_boxplot()

df.log.out |> ggplot(aes(x = EXP_IND, y = GESTTIME)) +
  geom_boxplot()

В основном видна монотонная зависимость, однако, предстоит проверить это с помощью критериев.

7. Нормальность, вид распределения

Из-за малости выборок в третьей и четвёртой группах исключим их из исследования.

get_df_on_ind <- function(i) {
  return(df.log.out |> filter(EXP_IND == i))
}

Посмотрим сначала на гистограммы распределений по группам для каждого признака.

for (i in c(1, 2, 5)) {
  print(paste("Группа ", i))
  par(mfrow = c(2, 4))
  hist(get_df_on_ind(i)$BODY_WEI, main = "")
  hist(get_df_on_ind(i)$BRAIN_WE, main = "")
  hist(get_df_on_ind(i)$SLOWWAVE, main = "")
  hist(get_df_on_ind(i)$PARADOX, main = "")
  hist(get_df_on_ind(i)$SLEEP, main = "")
  hist(get_df_on_ind(i)$LIFESPAN, main = "")
  hist(get_df_on_ind(i)$GESTTIME, main = "")
}
[1] "Группа  1"
[1] "Группа  2"

[1] "Группа  5"

Посмотрим на нормальность с помощью Normal Probability Plot:

for (i in c(1, 2, 5)) {
  print(paste("Группа ", i))
  par(mfrow = c(2, 4))
  plot_qq_graph(get_df_on_ind(i)$BODY_WEI, "BODY_WEI")
  plot_qq_graph(get_df_on_ind(i)$BRAIN_WE, "BRAIN_WE")
  plot_qq_graph(get_df_on_ind(i)$SLOWWAVE, "SLOWWAVE")
  plot_qq_graph(get_df_on_ind(i)$PARADOX, "PARADOX")
  plot_qq_graph(get_df_on_ind(i)$SLEEP, "SLEEP")
  plot_qq_graph(get_df_on_ind(i)$LIFESPAN, "LIFESPAN")
  plot_qq_graph(get_df_on_ind(i)$GESTTIME, "GESTTIME")
}
[1] "Группа  1"
[1] "Группа  2"

[1] "Группа  5"

И с помощью PP-plot:

for (i in c(1, 2, 5)) {
  print(paste("Группа ", i))
  par(mfrow = c(2, 4))
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(BODY_WEI)))$BODY_WEI, "BODY_WEI")
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(BRAIN_WE)))$BRAIN_WE, "BRAIN_WE")
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(SLOWWAVE)))$SLOWWAVE, "SLOWWAVE")
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(PARADOX)))$PARADOX, "PARADOX")
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(SLEEP)))$SLEEP, "SLEEP")
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(LIFESPAN)))$LIFESPAN, "LIFESPAN")
  plot_pp_plot((get_df_on_ind(i) |> filter(!is.na(GESTTIME)))$GESTTIME, "GESTTIME")
}
[1] "Группа  1"
[1] "Группа  2"

[1] "Группа  5"

А теперь оценим по тесту Шапиро-Уилка:

df.shapiro.test.p.value <- data.frame(ind = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))

for (i in c(1, 2, 5)) {
  df.shapiro.test.p.value[df.shapiro.test.p.value$ind == i, ] <- c(i, shapiro.test(get_df_on_ind(i)$BODY_WEI)$p.value, 
  shapiro.test(get_df_on_ind(i)$BRAIN_WE)$p.value,
  shapiro.test(get_df_on_ind(i)$SLOWWAVE)$p.value,
  shapiro.test(get_df_on_ind(i)$PARADOX)$p.value,
  shapiro.test(get_df_on_ind(i)$SLEEP)$p.value,
  shapiro.test(get_df_on_ind(i)$LIFESPAN)$p.value,
  shapiro.test(get_df_on_ind(i)$GESTTIME)$p.value)
}

df.shapiro.test.p.value |> print_df()
ind BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
1 0.1924485 0.4855703 0.4685556 0.0946346 0.3176217 0.0733372 0.2755650
2 0.0204661 0.0126712 0.4403360 0.8802017 0.4562102 0.8049845 0.5314537
5 0.9922347 0.7461442 0.0844404 0.9214902 0.0052604 0.4522227 0.0232529

И наконец по Лиллиефорсу:

df.lillie.test.p.value <- data.frame(ind = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))

for (i in c(1, 2, 5)) {
  df.lillie.test.p.value[df.lillie.test.p.value$ind == i, ] <- c(i, lillie.test(get_df_on_ind(i)$BODY_WEI)$p.value, 
  lillie.test(get_df_on_ind(i)$BRAIN_WE)$p.value,
  lillie.test(get_df_on_ind(i)$SLOWWAVE)$p.value,
  lillie.test(get_df_on_ind(i)$PARADOX)$p.value,
  lillie.test(get_df_on_ind(i)$SLEEP)$p.value,
  lillie.test(get_df_on_ind(i)$LIFESPAN)$p.value,
  lillie.test(get_df_on_ind(i)$GESTTIME)$p.value)
}

df.lillie.test.p.value |> print_df()
ind BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
1 0.3977150 0.3724944 0.2399545 0.2094751 0.2837660 0.3215462 0.4647484
2 0.1209793 0.0760938 0.8789409 0.9154588 0.8195576 0.7890638 0.9238088
5 0.7872499 0.4314403 0.0328885 0.5556462 0.0004679 0.4954892 0.1272532

Этот критерий является менее мощным, однако даёт схожие результаты.

По итогу можно считать, что распределения всех признаков во всех группах похожи на нормальные, кроме веса тела и веса мозга во второй группе и времени медленного сна, времени сна и времени беременности в пятой группе.

8. t-тесты

Сначала посмотрим на дисперсию, чтобы применить t-test для распределений с равными дисперсиями. Тест Фишера проверяет равенство дисперсий для нормально распределённых данных, которые мы определелили в прошлом пункте:

df.fisher.var <- data.frame(IND1 = rep(0, 3), IND2 = rep(0, 3), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))

k <- 1

for (i in c(1, 2, 5)){
  for (j in c(1, 2, 5)){
    if (i < j){
      df.fisher.var[k, ] <- c(i, j,
                                  var.test(get_df_on_ind(i)$BODY_WEI, get_df_on_ind(j)$BODY_WEI)$p.value,
                                  var.test(get_df_on_ind(i)$BRAIN_WE, get_df_on_ind(j)$BRAIN_WE)$p.value,
                                  var.test(get_df_on_ind(i)$SLOWWAVE, get_df_on_ind(j)$SLOWWAVE)$p.value,
                                  var.test(get_df_on_ind(i)$PARADOX, get_df_on_ind(j)$PARADOX)$p.value,
                                  var.test(get_df_on_ind(i)$SLEEP, get_df_on_ind(j)$SLEEP)$p.value,
                                  var.test(get_df_on_ind(i)$LIFESPAN, get_df_on_ind(j)$LIFESPAN)$p.value,
                                  var.test(get_df_on_ind(i)$GESTTIME, get_df_on_ind(j)$GESTTIME)$p.value)
      k <- k + 1
    }
  }
}

df.fisher.var |> print_df()
IND1 IND2 BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
1 2 0.3119679 0.1905823 0.3041400 0.4869923 0.6645465 0.9543801 0.7543563
1 5 0.6380604 0.3940484 0.4168502 0.7837348 0.0334406 0.0080450 0.6722005
2 5 0.6237712 0.6698967 0.1563733 0.4297079 0.0221523 0.0125095 0.5226789

Гипотеза о равенстве не отвергается для веса тела и веса мозга между первой и пятой группы, для времени медленного сна, времени сна и времени беременности между первой и второй группы, времени быстрого сна между всеми группами и продолжительности жизни между первой и второй группой.

Проведём t-тесты для первой, второй и пятой группы по всем признакам:

df.t.test.p.value <- data.frame(IND1 = rep(0, 3), IND2 = rep(0, 3), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))

k <- 1
flag <- FALSE
flagBB <- FALSE
flagSSG <-FALSE
flagL <- FALSE

for (i in c(1, 2, 5)){
  for (j in c(1, 2, 5)){
    if (i < j){
        flag <- i == 2 & j == 5
        flagBB <- i == 1 & j == 5
        flagSSG <- i == 1 & j == 2
        flagL <- i == 1 & j == 2
      
      df.t.test.p.value[k, ] <- c(i, j,
                                  t.test(get_df_on_ind(i)$BODY_WEI, get_df_on_ind(j)$BODY_WEI, var.equal = flag | flagBB)$p.value,
                                  t.test(get_df_on_ind(i)$BRAIN_WE, get_df_on_ind(j)$BRAIN_WE, var.equal = flag | flagBB)$p.value,
                                  t.test(get_df_on_ind(i)$SLOWWAVE, get_df_on_ind(j)$SLOWWAVE, var.equal = flag | flagSSG)$p.value,
                                  t.test(get_df_on_ind(i)$PARADOX, get_df_on_ind(j)$PARADOX, var.equal = TRUE)$p.value,
                                  t.test(get_df_on_ind(i)$SLEEP, get_df_on_ind(j)$SLEEP, var.equal = flag | flagSSG)$p.value,
                                  t.test(get_df_on_ind(i)$LIFESPAN, get_df_on_ind(j)$LIFESPAN, var.equal = flag | flagL)$p.value,
                                  t.test(get_df_on_ind(i)$GESTTIME, get_df_on_ind(j)$GESTTIME, var.equal = flag | flagSSG)$p.value)
      k <- k + 1
    }
  }
}

df.t.test.p.value |> print_df()
IND1 IND2 BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
1 2 0.3331031 0.8379613 0.4313905 0.0894578 0.3325090 0.4306579 0.2064574
1 5 0.0000011 0.0000094 0.0000195 0.0000105 0.0000000 0.0000080 0.0000807
2 5 0.0000004 0.0000050 0.0045885 0.0168625 0.0000441 0.0026227 0.0033338

Гипотеза о равенстве распределений по всем признакам между первой и второй не отвергается, а между первой и пятой и второй и пятой отвергается. Однако для признаков, которые мы посчитали ненормальными (вес тела и вес мозга во второй группе и время медленного сна, время сна и время беременности в пятой группе) p-value нельзя считать верным, так как размеры выборок малы и эти признаки не имеют нормального распределения.

9. Тест Манна-Уитни

Для проверки равенства распределений для ненормально распределённых признаков можем применить критерий Манна-Уитни, однако прежде посмотрим на симметричност по группам:

df.skew <- data.frame(IND = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))

for (i in c(1, 2, 5)){
  df.skew[df.skew$IND == i, ] <- c(i, skewness(get_df_on_ind(i)$BODY_WEI, na.rm = TRUE),
                                      skewness(get_df_on_ind(i)$BRAIN_WE, na.rm = TRUE),
                                      skewness(get_df_on_ind(i)$SLOWWAVE, na.rm = TRUE),
                                      skewness(get_df_on_ind(i)$PARADOX, na.rm = TRUE),
                                      skewness(get_df_on_ind(i)$SLEEP, na.rm = TRUE),
                                      skewness(get_df_on_ind(i)$LIFESPAN, na.rm = TRUE),
                                      skewness(get_df_on_ind(i)$GESTTIME, na.rm = TRUE))
}

df.skew |> print_df()
IND BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
1 0.1672298 0.5645030 -0.0002586 -0.5598674 0.3874961 1.0385890 0.5038765
2 -1.4951275 -1.6495435 0.5921447 -0.1315432 0.1049490 0.0122452 0.1394734
5 -0.1198309 0.0708243 1.0734624 -0.1861544 1.5538884 0.5578426 -1.0319607

Видим, что асимметрия веса тела и веса мозга у второй группы (ненормально распределённые признаки) сильно (примерно на порядок) отличается от первой и пятой группы. Аналогично: асимметрия времени сна и времени беременности пятой группы сильно отличается от первой и пятой. Для асимметрии времени быстрого сна у пятой группы и первой также нет оснований говорить о приблизительном равенстве, однако между пятой и второй группой разница мала, меньше \(0.5\), что может говорить об удовлетворении условий для критерия, однако надо ещё посмотреть на исправленную выборочную дисперсию:

df.var <- data.frame(IND = c(1, 2, 5), BODY_WEI_P = rep(0, 3), BRAIN_WE_P = rep(0, 3), SLOWWAVE_P = rep(0, 3), PARADOX_P = rep(0, 3), SLEEP_P = rep(0, 3), LIFESPAN_P = rep(0, 3), GESTTIME_P = rep(0, 3))

for (i in c(1, 2, 5)){
  df.var[df.var$IND == i, ] <- c(i, var(get_df_on_ind(i)$BODY_WEI, na.rm = TRUE),
                                    var(get_df_on_ind(i)$BRAIN_WE, na.rm = TRUE),
                                    var(get_df_on_ind(i)$SLOWWAVE, na.rm = TRUE),
                                    var(get_df_on_ind(i)$PARADOX, na.rm = TRUE),
                                    var(get_df_on_ind(i)$SLEEP, na.rm = TRUE),
                                    var(get_df_on_ind(i)$LIFESPAN, na.rm = TRUE),
                                    var(get_df_on_ind(i)$GESTTIME, na.rm = TRUE))
}

df.var |> print_df()
IND BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
1 6.034299 4.526511 7.279905 0.3159571 12.914130 0.9033046 0.8095168
2 3.443109 2.189395 12.449889 0.4525376 15.658077 0.9042128 0.6633179
5 4.599834 2.815790 3.733333 0.2533922 3.156556 0.1911274 0.9828249

Видим, что дисперсии времени медленного сна для пятой и второй группы даже приблизительно не равны, поэтому ни один из признаков, которые мы хотели проверить не подходят для применения критерия Манна-Уитни. То есть для них мы ничего не можем сказать о равенстве распределений.

10. Коэффициент Пирсона

pairwise.cor <- function(data, name_method = "pearson"){
  m <- cor(data)
  m.p.value <- cor(data)
  
  for (i in colnames(data)){
    for (j in colnames(data)){
      data.out <- data|> filter(!is.na(data[[i]]) & !is.na(data[[j]]))
      m[i, j] <- cor(x = data.out[[i]], y = data.out[[j]], method = name_method)
      m.p.value[i, j] <- cor.test(x = as.vector(data.out[[i]]), y = as.vector(data.out[[j]]), method = name_method)$p.value
    }
  }
  
  corrplot(m, method = "number")
  as.data.frame(m.p.value)
}

Перед вычислением коэффициента корреляции Пирсона посмотрим на нормальность признаков для всех индивидов по тесту Шапиро-Уилка:

df.shapiro.test.all.p.value <- data.frame(BODY_WEI_P = 0, BRAIN_WE_P = 0, SLOWWAVE_P = 0, PARADOX_P = 0, SLEEP_P = 0, LIFESPAN_P = 0, GESTTIME_P = 0)

for (i in c(1)) {
  df.shapiro.test.all.p.value[1, ] <- c(shapiro.test(df.log.out$BODY_WEI)$p.value, 
  shapiro.test(df.log.out$BRAIN_WE)$p.value,
  shapiro.test(df.log.out$SLOWWAVE)$p.value,
  shapiro.test(df.log.out$PARADOX)$p.value,
  shapiro.test(df.log.out$SLEEP)$p.value,
  shapiro.test(df.log.out$LIFESPAN)$p.value,
  shapiro.test(df.log.out$GESTTIME)$p.value)
}

df.shapiro.test.all.p.value |> print_df()
BODY_WEI_P BRAIN_WE_P SLOWWAVE_P PARADOX_P SLEEP_P LIFESPAN_P GESTTIME_P
0.6745281 0.7912255 0.7502073 0.8786641 0.2600607 0.334608 0.1265908

Как видим по p-value можем считать, что распределения похожи на нормальные, поэтому критерий проверки на значимость коэффициента корреляции будет проверять ещё и независимость признаков, а также коэффициент Спирмена будет примерно равен коэффициенту Пирсона.

Теперь посчитаем коэффициенты корреляции Пирсона для всех пар признаков, причём удаление NA будем производить попарно, чтобы для каждой пары получить максимальное количество индивидов:

df.log.out |> 
  mutate(BRAIN_BODY = log(exp(BRAIN_WE) / exp(BODY_WEI))) |>
  dplyr::select(-NAME, -EXP_IND, -PRED_IND, -DANG_IND) |> 
  pairwise.cor() |> 
  print_df()
BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME BRAIN_BODY
BODY_WEI 0.0000000 0.0000000 0.0003011 0.0182618 0.0002898 0.0000000 0.0000000 0.0000000
BRAIN_WE 0.0000000 0.0000000 0.0004412 0.0043862 0.0000914 0.0000000 0.0000000 0.0000397
SLOWWAVE 0.0003011 0.0004412 0.0000000 0.0000332 0.0000000 0.0008653 0.0000535 0.0414927
PARADOX 0.0182618 0.0043862 0.0000332 0.0000000 0.0000000 0.0060667 0.0000022 0.7322638
SLEEP 0.0002898 0.0000914 0.0000000 0.0000000 0.0000000 0.0000957 0.0000000 0.1188045
LIFESPAN 0.0000000 0.0000000 0.0008653 0.0060667 0.0000957 0.0000000 0.0000000 0.0241044
GESTTIME 0.0000000 0.0000000 0.0000535 0.0000022 0.0000000 0.0000000 0.0000000 0.0144867
BRAIN_BODY 0.0000000 0.0000397 0.0414927 0.7322638 0.1188045 0.0241044 0.0144867 0.0000000

Видим, что для всех пар признаков критерий даёт p-value меньше \(\alpha = 0.05\), то есть отвергаем гипотезу о незначимости зависимсоти для этих признаков.

Зависимости:

  1. Вес мозга зависит от веса тела;

  2. Общая продолжительность сна зависит от медленного и быстрого сна, а также они зависят друг от друга, но скорее косвенно, так как это соотношение скорее индивидуально для каждого животного;

  3. Время беременности зависит от продолжительности жизни, так как беременность не должна занимать большей части жизни из-за уязвимости такого животного;

  4. Чем больше животное, тем дольше беременность и продолжительность жизни, так как крупное животное требует как большого формирования во время беременности, так как и большего времени взросления;

  5. Также от размера мозга зависит продолжительность жизни, однако эта зависимость не является прямой, так как если мы рассмотрим долю веса мозга к весу тела, то корреляция значительно упадёт;

  6. Зависимости между сном и весом тела, сном и продолжительностью жизни или сном и времени беременности достаточно сложно объяснить, для этого нужно углублятся в сложные биологичесике процессы.

11. Коэффициент Спирмена

Проверим, что ранговый коэффициент корреляции Спирмена примерно равен Пирсону:

df.log.out |> 
 dplyr::select(-NAME, -EXP_IND, -PRED_IND, -DANG_IND) |> 
  pairwise.cor(name_method = "spearman") |> 
  print_df()
BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME
BODY_WEI 0.0000000 0.0000000 0.0019593 0.0063690 0.0004095 0.0000000 0.00e+00
BRAIN_WE 0.0000000 0.0000000 0.0008980 0.0004787 0.0000531 0.0000000 0.00e+00
SLOWWAVE 0.0019593 0.0008980 0.0000000 0.0000730 0.0000000 0.0010380 1.59e-05
PARADOX 0.0063690 0.0004787 0.0000730 0.0000000 0.0000000 0.0026538 2.60e-06
SLEEP 0.0004095 0.0000531 0.0000000 0.0000000 0.0000000 0.0000883 1.00e-07
LIFESPAN 0.0000000 0.0000000 0.0010380 0.0026538 0.0000883 0.0000000 0.00e+00
GESTTIME 0.0000000 0.0000000 0.0000159 0.0000026 0.0000001 0.0000000 0.00e+00

Результаты схожи и это не удивительно, ведь мы считаем, что данные похожи на нормальные, а для них отличие коэффициента Пирсона от Спирмена незначительно.

12. Частные корреляционные отношения

Однако возникает подозрение, что многие зависимости имеют другую причину, нежели зависимость только друг от друга. Как было видно по ящикам с усами для индекса опасности места для сна есть монотонная зависимость почти всех признаков от этого индекса. Поэтому возникает идея проверки коэффициента частной корреляции с вычетом влияния этого индекса.

df.log.out |> 
  group_by(EXP_IND) |> 
  mutate(BODY_WEI = BODY_WEI - mean(BODY_WEI, na.rm = TRUE), BRAIN_WE = BRAIN_WE - mean(BRAIN_WE, na.rm = TRUE), SLOWWAVE = SLOWWAVE - mean(SLOWWAVE, na.rm = TRUE), PARADOX = PARADOX - mean(PARADOX, na.rm = TRUE), SLEEP = SLEEP - mean(SLEEP, na.rm = TRUE), LIFESPAN = LIFESPAN - mean(LIFESPAN, na.rm = TRUE), GESTTIME = GESTTIME - mean(GESTTIME, na.rm = TRUE)) |>
  group_by(.drop = TRUE) |> dplyr::select(-NAME, -EXP_IND, -PRED_IND, -DANG_IND) |> 
  pairwise.cor() |> 
  print_df()
BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME
BODY_WEI 0.0000000 0.0000000 0.1044414 0.3927193 0.5207584 0.0000000 0.0000003
BRAIN_WE 0.0000000 0.0000000 0.0715185 0.9971036 0.1841235 0.0000000 0.0000000
SLOWWAVE 0.1044414 0.0715185 0.0000000 0.0055510 0.0000000 0.0723565 0.0053589
PARADOX 0.3927193 0.9971036 0.0055510 0.0000000 0.0000016 0.7878256 0.0094343
SLEEP 0.5207584 0.1841235 0.0000000 0.0000016 0.0000000 0.0607429 0.0002778
LIFESPAN 0.0000000 0.0000000 0.0723565 0.7878256 0.0607429 0.0000000 0.0000633
GESTTIME 0.0000003 0.0000000 0.0053589 0.0094343 0.0002778 0.0000633 0.0000000

Здесь мы видим, что достаточно много зависимостей перестали быть значимыми (\(\alpha = 0.05\)), так как мы исключили влияние индекса опасности места для сна. Ожидаемо зависимости всех видов сна от веса тела, веса мозга и продолжительности сна значительно ослабли, однако зависимости сна от времени беременности уменьшились мало.

13. Множественная регрессия

Посмотрим на данные:

df.log.out |> dplyr::select(-NAME) |> ggpairs(columns = 1:7)

Всё, что нужно, было прологарифмировано раньше, поэтому приступим к регрессии. Хотим прогнозировать время жизни по остальным данным.

Строим регрессию по всем признакам:

df.log.out.na <- df.log.out |> dplyr::filter(!is.na(SLOWWAVE) & !is.na(PARADOX) & !is.na(SLEEP) & !is.na(LIFESPAN) & !is.na(GESTTIME))
model <- lm(LIFESPAN ~ ., df.log.out.na |> dplyr::select(-NAME))
model.beta <- lm.beta(model)
summary(model.beta)

Call:
lm(formula = LIFESPAN ~ ., data = dplyr::select(df.log.out.na, 
    -NAME))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77116 -0.16275  0.02196  0.12238  0.51269 

Coefficients:
             Estimate Standardized Std. Error t value Pr(>|t|)    
(Intercept)  1.348343           NA   0.903753   1.492 0.151321    
BODY_WEI    -0.022443    -0.065240   0.096158  -0.233 0.817825    
BRAIN_WE     0.461812     1.096994   0.104491   4.420 0.000264 ***
SLOWWAVE     0.095508     0.327255   0.176317   0.542 0.594021    
PARADOX      0.121882     0.080915   0.338311   0.360 0.722426    
SLEEP       -0.050191    -0.215930   0.181925  -0.276 0.785466    
GESTTIME    -0.099790    -0.105443   0.159882  -0.624 0.539586    
PRED_IND2   -0.882934    -0.383209   0.482871  -1.829 0.082427 .  
PRED_IND3   -1.451831    -0.553794   0.624294  -2.326 0.030675 *  
PRED_IND4   -1.151949    -0.439406   0.754374  -1.527 0.142415    
PRED_IND5   -1.098412    -0.459997   0.796382  -1.379 0.183047    
EXP_IND2     0.069167     0.027761   0.224152   0.309 0.760836    
EXP_IND3     0.027348     0.008247   0.293812   0.093 0.926767    
EXP_IND4    -0.385732    -0.116321   0.421313  -0.916 0.370812    
EXP_IND5     0.003063     0.001169   0.555897   0.006 0.995658    
DANG_IND2    0.703874     0.294771   0.457148   1.540 0.139305    
DANG_IND3    1.044139     0.398282   0.624193   1.673 0.109941    
DANG_IND4    1.275729     0.534255   0.811954   1.571 0.131827    
DANG_IND5    1.162274     0.386226   1.122186   1.036 0.312693    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3471 on 20 degrees of freedom
Multiple R-squared:  0.939, Adjusted R-squared:  0.884 
F-statistic: 17.09 on 18 and 20 DF,  p-value: 1.994e-08

Регрессия значима, но значимых коэффициентов на уровне \(\alpha = 0.05\) только два: для веса мозга и для фиктивной переменной индекса хищничества при значении \(3\).

14. Доверительные эллипсы

Посчитаем ковариационную и корреляционную матрицу для коэффициентов регрессии:

dummy.variable <- data.frame(NAME = df.log.out.na$NAME)

for (i in 2:5){
  dummy.variable[[paste("PRED_IND", i, sep = "_")]] = ifelse(df.log.out.na$PRED_IND == i, 1, 0)
}
for (i in 2:5){
  dummy.variable[[paste("EXP_IND", i, sep = "_")]] = ifelse(df.log.out.na$EXP_IND == i, 1, 0)
}
for (i in 2:5){
  dummy.variable[[paste("DANG_IND", i, sep = "_")]] = ifelse(df.log.out.na$DANG_IND == i, 1, 0)
}

n.df <- length(df.log.out.na$NAME)
sigma.df <- sum(model$residuals ** 2) / (n.df - model$rank)
covMatrix.df <- df.log.out.na |> dplyr::select(-NAME, -PRED_IND, -EXP_IND, -DANG_IND) |> cbind(dummy.variable |> dplyr::select(-NAME)) |> cov()
cov_b.df <- sigma.df / n.df * solve(covMatrix.df)
cov_beta.df <- sigma.df / n.df * solve(cov2cor(covMatrix.df))
cor_b.df <- cov2cor(cov_b.df)
corrplot(cor_b.df, method = "color")

Видим сильную корреляцию между между оценками коэффициентов регрессии между весом тела и мозга, между всеми видами сна, между индексами общей опасности и хищничества, а также между весом мозга и продолжительностью жизни.

Посмотрим на доверительный эллипсоид для значимых коэффициентов, обозначенных выше:

plot(ellipse::ellipse(cov_beta.df[c(2, 9), c(2, 9)], centre = model.beta$standardized.coefficients[c(3, 9)], level=0.95, npoints = 100), type = "l", asp = 1)
lines(x = c(-2, 2), y = c(2, -2), col = "red")

Он имеет плохой вид: либо оба имеет сильное влияние, либо оба — слабое. Причём индекс хищничества может быть совсем не значим, так как эллипс пересекает нулевое значение по \(y\).

15. Сильно коррелирующие признаки

Построим таблицу VIF:

ols_vif_tol(model)

Наблюдаем очень большую мультиколлинеарность почти по всем признакам.

Теперь таблица с частичными корреляциями:

ols_correlations(model)
                Correlations                 
--------------------------------------------
Variable     Zero Order    Partial     Part  
--------------------------------------------
BODY_WEI          0.842     -0.052    -0.013 
BRAIN_WE          0.933      0.703     0.244 
SLOWWAVE         -0.497      0.120     0.030 
PARADOX          -0.369      0.080     0.020 
SLEEP            -0.517     -0.062    -0.015 
GESTTIME          0.737     -0.138    -0.034 
PRED_IND2        -0.209     -0.378    -0.101 
PRED_IND3        -0.241     -0.461    -0.128 
PRED_IND4         0.075     -0.323    -0.084 
PRED_IND5         0.090     -0.295    -0.076 
EXP_IND2         -0.243      0.069     0.017 
EXP_IND3          0.220      0.021     0.005 
EXP_IND4          0.158     -0.201    -0.051 
EXP_IND5          0.455      0.001     0.000 
DANG_IND2        -0.215      0.326     0.085 
DANG_IND3        -0.399      0.350     0.092 
DANG_IND4         0.218      0.331     0.087 
DANG_IND5         0.307      0.226     0.057 
--------------------------------------------

Сильное влияние на зависимую переменную за исключением остальных оказывают, в перую очередь, вес мозга, а также индекс хищничества и общий индекс опасности (хотя он сильно зависит от индекса хищничества).

Попробуем убрать сильно коррелирующие. Уберём вес тела, общее время сна и общий индекс опасности:

model.minuscor <- lm(LIFESPAN ~ ., df.log.out.na |> dplyr::select(-NAME, -BODY_WEI, - SLEEP, -DANG_IND))
model.minuscor.beta <- lm.beta(model.minuscor)
summary(model.minuscor.beta)

Call:
lm(formula = LIFESPAN ~ ., data = dplyr::select(df.log.out.na, 
    -NAME, -BODY_WEI, -SLEEP, -DANG_IND))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.70225 -0.16574  0.03341  0.20078  0.47870 

Coefficients:
             Estimate Standardized Std. Error t value Pr(>|t|)    
(Intercept)  1.552951           NA   0.629838   2.466   0.0206 *  
BRAIN_WE     0.427375     1.015193   0.061132   6.991 2.01e-07 ***
SLOWWAVE     0.023400     0.080180   0.029182   0.802   0.4299    
PARADOX     -0.079520    -0.052792   0.153749  -0.517   0.6094    
GESTTIME    -0.087863    -0.092841   0.130827  -0.672   0.5078    
PRED_IND2   -0.371255    -0.161131   0.227439  -1.632   0.1147    
PRED_IND3   -0.534832    -0.204009   0.240393  -2.225   0.0350 *  
PRED_IND4   -0.062608    -0.023882   0.326654  -0.192   0.8495    
PRED_IND5   -0.053963    -0.022599   0.345540  -0.156   0.8771    
EXP_IND2     0.206412     0.082846   0.184211   1.121   0.2727    
EXP_IND3     0.121182     0.036543   0.258597   0.469   0.6433    
EXP_IND4    -0.189088    -0.057021   0.323711  -0.584   0.5642    
EXP_IND5    -0.020639    -0.007873   0.357456  -0.058   0.9544    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3648 on 26 degrees of freedom
Multiple R-squared:  0.9123,    Adjusted R-squared:  0.8719 
F-statistic: 22.55 on 12 and 26 DF,  p-value: 1.012e-10

По значимости имеем ту же ситуацию, однако значимость по p-value подросла, а \(R^2\) почти не изменился.

Мультиколлинеарность исчезла:

ols_vif_tol(model.minuscor)

16. Пошаговая регрессия

Backward

Построим пошаговую регрессию. Сначала рассмотрим убавление количества параметров:

st.bw.lm <- ols_step_backward_p(model.minuscor)
st.bw.lm

                            Stepwise Summary                             
-----------------------------------------------------------------------
Step    Variable       AIC       SBC       SBIC        R2       Adj. R2 
-----------------------------------------------------------------------
 0      Full Model    44.212    67.502    -71.965    0.91234    0.87188 
 1      PARADOX       42.612    64.238    -74.719    0.91144    0.87536 
 2      GESTTIME      40.951    60.914    -77.453    0.91066    0.87875 
 3      EXP_IND       35.968    49.276    -78.073    0.90348    0.88538 
 4      SLOWWAVE      34.481    46.126    -80.367    0.90220    0.88738 
-----------------------------------------------------------------------

Final Model Output 
------------------

                        Model Summary                          
--------------------------------------------------------------
R                       0.950       RMSE                0.315 
R-Squared               0.902       MSE                 0.117 
Adj. R-Squared          0.887       Coef. Var          14.153 
Pred R-Squared          0.864       AIC                34.481 
MAE                     0.260       SBC                46.126 
--------------------------------------------------------------
 RMSE: Root Mean Square Error 
 MSE: Mean Square Error 
 MAE: Mean Absolute Error 
 AIC: Akaike Information Criteria 
 SBC: Schwarz Bayesian Criteria 

                               ANOVA                                
-------------------------------------------------------------------
               Sum of                                              
              Squares        DF    Mean Square      F         Sig. 
-------------------------------------------------------------------
Regression     35.614         5          7.123    60.884    0.0000 
Residual        3.861        33          0.117                     
Total          39.475        38                                    
-------------------------------------------------------------------

                                  Parameter Estimates                                    
----------------------------------------------------------------------------------------
      model      Beta    Std. Error    Std. Beta      t        Sig      lower     upper 
----------------------------------------------------------------------------------------
(Intercept)     1.534         0.175                  8.760    0.000     1.178     1.890 
   BRAIN_WE     0.375         0.024        0.890    15.606    0.000     0.326     0.424 
  PRED_IND2    -0.311         0.184       -0.135    -1.686    0.101    -0.685     0.064 
  PRED_IND3    -0.569         0.196       -0.217    -2.909    0.006    -0.968    -0.171 
  PRED_IND4    -0.150         0.193       -0.057    -0.777    0.443    -0.541     0.242 
  PRED_IND5    -0.114         0.183       -0.048    -0.625    0.536    -0.487     0.258 
----------------------------------------------------------------------------------------

Как это выглядит по шагам:

plot(st.bw.lm)

Какая же модель получается в итоге:

summary(st.bw.lm$model)

Call:
lm(formula = paste(response, "~", paste(c(include, cterms), collapse = " + ")), 
    data = l)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.7207 -0.2307 -0.0212  0.2462  0.5830 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.53397    0.17511   8.760 3.99e-10 ***
BRAIN_WE     0.37477    0.02401  15.606  < 2e-16 ***
PRED_IND2   -0.31054    0.18418  -1.686  0.10121    
PRED_IND3   -0.56931    0.19574  -2.909  0.00645 ** 
PRED_IND4   -0.14951    0.19252  -0.777  0.44291    
PRED_IND5   -0.11438    0.18295  -0.625  0.53612    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.342 on 33 degrees of freedom
Multiple R-squared:  0.9022,    Adjusted R-squared:  0.8874 
F-statistic: 60.88 on 5 and 33 DF,  p-value: 1.066e-15

Осталось \(2\) признака, удалились время беременности, быстрый сон, медленный сон и индекс воздействия сна.

Forward

Теперь будем добавлять признаки:

st.fw.lm <- ols_step_forward_p(model.minuscor)
st.fw.lm

                             Stepwise Summary                              
-------------------------------------------------------------------------
Step    Variable        AIC        SBC       SBIC        R2       Adj. R2 
-------------------------------------------------------------------------
 0      Base Model    115.149    118.476      1.181    0.00000    0.00000 
 1      BRAIN_WE       37.664     42.655    -73.233    0.86972    0.86620 
 2      PRED_IND       34.481     46.126    -80.767    0.90220    0.88738 
-------------------------------------------------------------------------

Final Model Output 
------------------

                        Model Summary                          
--------------------------------------------------------------
R                       0.950       RMSE                0.315 
R-Squared               0.902       MSE                 0.117 
Adj. R-Squared          0.887       Coef. Var          14.153 
Pred R-Squared          0.864       AIC                34.481 
MAE                     0.260       SBC                46.126 
--------------------------------------------------------------
 RMSE: Root Mean Square Error 
 MSE: Mean Square Error 
 MAE: Mean Absolute Error 
 AIC: Akaike Information Criteria 
 SBC: Schwarz Bayesian Criteria 

                               ANOVA                                
-------------------------------------------------------------------
               Sum of                                              
              Squares        DF    Mean Square      F         Sig. 
-------------------------------------------------------------------
Regression     35.614         5          7.123    60.884    0.0000 
Residual        3.861        33          0.117                     
Total          39.475        38                                    
-------------------------------------------------------------------

                                  Parameter Estimates                                    
----------------------------------------------------------------------------------------
      model      Beta    Std. Error    Std. Beta      t        Sig      lower     upper 
----------------------------------------------------------------------------------------
(Intercept)     1.534         0.175                  8.760    0.000     1.178     1.890 
   BRAIN_WE     0.375         0.024        0.890    15.606    0.000     0.326     0.424 
  PRED_IND2    -0.311         0.184       -0.135    -1.686    0.101    -0.685     0.064 
  PRED_IND3    -0.569         0.196       -0.217    -2.909    0.006    -0.968    -0.171 
  PRED_IND4    -0.150         0.193       -0.057    -0.777    0.443    -0.541     0.242 
  PRED_IND5    -0.114         0.183       -0.048    -0.625    0.536    -0.487     0.258 
----------------------------------------------------------------------------------------

Как это выглядело на графике:

plot(st.fw.lm)

Какую же модель получили:

summary(st.fw.lm$model)

Call:
lm(formula = paste(response, "~", paste(preds, collapse = " + ")), 
    data = l)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.7207 -0.2307 -0.0212  0.2462  0.5830 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.53397    0.17511   8.760 3.99e-10 ***
BRAIN_WE     0.37477    0.02401  15.606  < 2e-16 ***
PRED_IND2   -0.31054    0.18418  -1.686  0.10121    
PRED_IND3   -0.56931    0.19574  -2.909  0.00645 ** 
PRED_IND4   -0.14951    0.19252  -0.777  0.44291    
PRED_IND5   -0.11438    0.18295  -0.625  0.53612    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.342 on 33 degrees of freedom
Multiple R-squared:  0.9022,    Adjusted R-squared:  0.8874 
F-statistic: 60.88 on 5 and 33 DF,  p-value: 1.066e-15

Она аналогична той, которую получили от backward.

Однако только при одном значении индекса хищничества коэффициент значимый, поэтому вручную создадим фиктивные переменные и проведём опять автоматический отбор:

df.log.out.dummy <- df.log.out.na
for (i in 2:5){
  df.log.out.dummy[[paste("PRED_IND", i, sep = "_")]] <- ifelse(df.log.out.dummy$PRED_IND == i, 1, 0)
}
model.out.dummy <- lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3 + PRED_IND_4 + PRED_IND_5, data = df.log.out.dummy)
st.bw.lm.dummy <- ols_step_backward_p(model.out.dummy)
plot(st.bw.lm.dummy)

summary(st.bw.lm.dummy$model)

Call:
lm(formula = paste(response, "~", paste(c(include, cterms), collapse = " + ")), 
    data = l)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.63197 -0.23515 -0.04439  0.22367  0.67817 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.42794    0.10776  13.251 3.35e-15 ***
BRAIN_WE     0.37812    0.02315  16.332  < 2e-16 ***
PRED_IND_2  -0.21197    0.13117  -1.616  0.11507    
PRED_IND_3  -0.47162    0.14733  -3.201  0.00291 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3354 on 35 degrees of freedom
Multiple R-squared:  0.9003,    Adjusted R-squared:  0.8917 
F-statistic: 105.3 on 3 and 35 DF,  p-value: < 2.2e-16

Осталось два значения индекса хищничества.

17. Остатки и Predicted vs Residuals

Нашли лучшую модель регрессии. Добавим те наблюдения, которые имели пропущенные значения только на убранных данных:

df.log.na.best <- df.log |> filter(!is.na(LIFESPAN))
for (i in 2:5){
  df.log.na.best[[paste("PRED_IND", i, sep = "_")]] <- ifelse(df.log.na.best$PRED_IND == i, 1, 0)
}
model.best <- lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best)
model.beta.best <- lm.beta(model.best)
summary(model.beta.best)

Call:
lm(formula = LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.12708 -0.35389 -0.02041  0.23742  1.77925 

Coefficients:
            Estimate Standardized Std. Error t value Pr(>|t|)    
(Intercept)  1.80029           NA    0.14546  12.377  < 2e-16 ***
BRAIN_WE     0.28962      0.76276    0.03015   9.605 2.79e-13 ***
PRED_IND_2  -0.22525     -0.10042    0.18422  -1.223  0.22676    
PRED_IND_3  -0.52686     -0.22082    0.19306  -2.729  0.00856 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5536 on 54 degrees of freedom
Multiple R-squared:  0.6738,    Adjusted R-squared:  0.6557 
F-statistic: 37.19 on 3 and 54 DF,  p-value: 3.589e-13

Ясно, что согласованность модели упала, так как мы построили её на большем наборе данных (плюс были добавлены выбросы, удаленные раньше).

Теперь посмотрим на нормальность остатков (хотим точность проверяемых критериев):

plot(model.best, which=2)

Видим, что справа нормальности не наблюдается, хотя на основном массиве данных нормальность присутствует.

Теперь посмотрим на график Predicted vs Residuals:

plot(model.best,which=1)

Видим достаточно равномерное распределение в прямоугольной области около нуля. То есть можно предположить адекватность выбранной линейной модели, а также гомогедостичность (то есть равенство дисперсий остатков).

График Residuls vs Deleted Residuals:

del_res <- sapply(1:58, function(n) df.log.na.best$LIFESPAN[n] - predict(lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best[-n, ]), df.log.na.best[n, ]))
plot(x = model.best$residuals, y = del_res, xlab = "Rsiduals", ylab = "Deleted residuals")
lines(x = c(-3, 3), y = c(-3, 3))
abline(lm(del_res ~ model.best$residuals), col = "blue")
text(x = model.best$residuals, y = del_res, labels = rownames(df.log.na.best), cex = 0.6, pos = 4, col = "red")

Как и ожидалось, точки расположились под чуть большим наклоном, нежели \(y = x\). Больших отклонений (по линии регресии на этих точках) не видно, то есть сказать что-то о выбросах нельзя.

18. Выбросы по Махаланобису

df.log.na.best.x <- df.log.na.best |> dplyr::select(BRAIN_WE, PRED_IND_2, PRED_IND_3)

cov_matrix <- cov(df.log.na.best.x)

mahalanobis_distance <- mahalanobis(df.log.na.best.x, center = colMeans(df.log.na.best.x), cov = cov_matrix)

plot(mahalanobis_distance, 
     main = "Mahalanobis Distance Plot",
     xlab = "Observation",
     ylab = "Mahalanobis Distance",
     type = "h", 
     col = "darkblue") 

plot(sort(mahalanobis_distance), 
     main = "Mahalanobis Distance Plot",
     xlab = "Observation",
     ylab = "Mahalanobis Distance",
     type = "h", 
     col = "darkblue") 

По Махаланобису виден скачок для двух наблюдений: \(1\) и \(4\). Это два слона:

NAME BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME PRED_IND EXP_IND DANG_IND PRED_IND_2 PRED_IND_3 PRED_IND_4 PRED_IND_5
African elephant 8.802974 8.650324 NA NA 3.3 3.653252 6.46925 3 5 3 0 1 0 0
Asian elephant 7.842671 8.434463 2.1 0.5877867 3.9 4.234107 6.43615 3 5 4 0 1 0 0

19. Выбросы по Куку

cooks_distance <- cooks.distance(model.best)

plot(cooks_distance, 
     col = "darkblue",
     type = "h", 
     main = "Cook's Distance",
     xlab = "Observation",
     ylab = "Cook's Distance")

plot(sort(cooks_distance), 
     col = "darkblue",
     type = "h", 
     main = "Cook's Distance",
     xlab = "Observation",
     ylab = "Cook's Distance")

По скачку значения расстояния Кука можно выделить три наблюдения: 6, 14 и 31. Это те самые летучие мыши и ещё ехидна:

df.log.na.best[c(6, 14, 31), ] |> print_df()
NAME BODY_WEI BRAIN_WE SLOWWAVE PARADOX SLEEP LIFESPAN GESTTIME PRED_IND EXP_IND DANG_IND PRED_IND_2 PRED_IND_3 PRED_IND_4 PRED_IND_5
Big brown bat -3.772261 -1.203973 15.8 1.3609766 19.7 2.944439 3.555348 1 1 1 0 0 0 0
Echidna 1.098612 3.218876 8.6 NA 8.6 3.912023 3.332205 2 2 2 1 0 0 0
Little brown bat -4.605170 -1.386294 17.9 0.6931472 19.9 3.178054 3.912023 1 1 1 0 0 0 0

20. Studentized Residuals vs Leverage Plot

Наконец посмотрим на соотношение рычага и остатков:

ols_plot_resid_lev(model.best)

Здесь явных выбросов нет: по остаткам, за пределами доверительного интервала небольшое количество наблюдений, по рычагу два кандидата на выбросы — слоны, а наблюдений, которые бы были и по рычагу и по остаткам “выбросами”, нет.

21. Исключаем выбросы

Найденные выбросы имеют определённый смысл — большие и средне атакуемые слоны, маленькие, но долгоживущие летучие мыши и такая же ехидна. Слонов исключать не будем, так как они просто большие, поэтому Махаланобис их выделяет, однако они хорошо согласуются с моделью (видно по картинке, например). Исключим выбросы:

model.best.out <- lm(LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best[c(-6, -14, -31),])
model.best.out.beta <- lm.beta(model.best.out)
summary(model.best.out.beta)

Call:
lm(formula = LIFESPAN ~ BRAIN_WE + PRED_IND_2 + PRED_IND_3, data = df.log.na.best[c(-6, 
    -14, -31), ])

Residuals:
    Min      1Q  Median      3Q     Max 
-1.0491 -0.2596  0.0245  0.2405  1.0616 

Coefficients:
            Estimate Standardized Std. Error t value Pr(>|t|)    
(Intercept)  1.47436           NA    0.11487  12.835   <2e-16 ***
BRAIN_WE     0.34604      0.87394    0.02316  14.941   <2e-16 ***
PRED_IND_2  -0.15023     -0.06611    0.13724  -1.095    0.279    
PRED_IND_3  -0.36992     -0.15766    0.13833  -2.674    0.010 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3905 on 51 degrees of freedom
Multiple R-squared:  0.8394,    Adjusted R-squared:   0.83 
F-statistic: 88.87 on 3 and 51 DF,  p-value: < 2.2e-16

Судя по \(R^2\) и его исправленному варианту, а также по p-value значимости регрессии, получили более хорошую модель.

22. Предсказания

Предскажем время жизниобыкновенного ежа. Вес его мозга\(3.3\) г, имеет защиту от хищников, но крупные хищные птицы успешно их атакуют, поэтому индекс хищничества — примерно \(3\). В неволе ежи живут \(4\)-\(6\) лет. Пусть будет \(5\):

data.hedgehog <- data.frame(NAME = "European hedgehog", BRAIN_WE = log(3.3), LIFESPAN = log(5), PRED_IND_2 = 0, PRED_IND_3 = 1)
data.hedgehog |> print_df()
NAME BRAIN_WE LIFESPAN PRED_IND_2 PRED_IND_3
European hedgehog 1.193922 1.609438 0 1

Предскажем его продолжительность жизни:

pred.hedgehog.pred <- predict(model.best.out, newdata = data.hedgehog, interval = "predict")
pred.hedgehog.conf <- predict(model.best.out, newdata = data.hedgehog, interval = "confidence")
exp(pred.hedgehog.pred[, 1])
[1] 4.561225

Получили цифры близкие к действительности. Посмотрим на предсказательный интервал:

print("Нижняя граница:")
[1] "Нижняя граница:"
exp(pred.hedgehog.pred[, 2])
[1] 2.002536
print("Верхняя граница:")
[1] "Верхняя граница:"
exp(pred.hedgehog.pred[, 3])
[1] 10.38922

Ежи в неволе могут жить до 10 лет.

А теперь на доверительный:

print("Нижняя граница:")
[1] "Нижняя граница:"
exp(pred.hedgehog.conf[, 2])
[1] 3.549484
print("Верхняя граница:")
[1] "Верхняя граница:"
exp(pred.hedgehog.conf[, 3])
[1] 5.861353
LS0tCnRpdGxlOiAiU2xlZXAiCmF1dGhvcjogIk9sZXluaWsgTWljaGFlbCIKZGF0ZTogIjIwMjMtMTEtMjAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIDAuINCR0LjQsdC70LjQvtGC0LXQutC4INC4INC/0YDQtdC00LLQsNGA0LjRgtC10LvRjNC90LDRjyDQv9C+0LTQs9C+0YLQvtCy0LrQsAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2NhdHRlclBsb3RNYXRyaXgpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShwc3ljaCkKbGlicmFyeShzdW1tYXJ5dG9vbHMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkobm9ydGVzdCkKbGlicmFyeShtb21lbnRzKQpsaWJyYXJ5KHBwY29yKQpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KGxtLmJldGEpCmxpYnJhcnkoZWxsaXBzZSkKbGlicmFyeShjYXIpCmxpYnJhcnkob2xzcnIpCnByaW50X2RmIDwtIGZ1bmN0aW9uKGRmKQp7CiAgZGYgfD4KICAgIGthYmxlKGZvcm1hdCA9ICJodG1sIikgfD4KICAgIGthYmxlX3N0eWxpbmcoKSB8PgogICAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjEwMCUiKQp9CgpwbG90X3FxX2dyYXBoIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHVtbl9uYW1lKSB7CiAgZGF0YTwtZGF0YQogIGV4cGVjdGVkX3F1YW50aWxlcyA8LSBxbm9ybShwcG9pbnRzKGxlbmd0aChkYXRhKSkpCiAgcXFub3JtKGRhdGEsICBwY2ggPSAxOSwgY29sID0gImRlZXBwaW5rIikKICBxcWxpbmUoZGF0YSwgZGlzdHJpYnV0aW9uID0gcW5vcm0sIGx3ZCA9IDIsIGNvbCA9ICJsaW1lZ3JlZW4iKQogIGxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IGNvbHVtbl9uYW1lLCBjb2wgPSAiZGVlcHBpbmsiLCBwY2ggPSAxOSkKfQoKcGxvdF9wcF9wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHVtbl9uYW1lKSB7CiAgbiA8LSBsZW5ndGgoZGF0YSkKICBleHBlY3RfcHJvYiA8LSBwbm9ybShkYXRhLCBtZWFuKGRhdGEpLCBzcXJ0KHZhcihkYXRhKSAqIChuIC0gMSkgLyBuKSkKICBleHBlY3RfcHJvYiA8LSBzb3J0KGV4cGVjdF9wcm9iKQogIHBsb3QoeCA9IGV4cGVjdF9wcm9iLCB5ID0gcHBvaW50cyhuKSwgY29sID0gInJlZCIpCiAgbGluZXMoeCA9IGMoMCwgMSksIHkgPSBjKDAsIDEpLCBjb2wgPSAiYmx1ZSIpCiAgbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gY29sdW1uX25hbWUsIGNvbCA9ICJkZWVwcGluayIsIHBjaCA9IDE5KQp9CmBgYAoKIyMgMS4g0JfQsNCz0YDRg9C30LrQsCDQuCDQv9GA0LXQtNC+0LHRgNCw0LHQvtGC0LrQsCDQtNCw0L3QvdGL0YUKCmBgYHtyfQpkZiA8LSByZWFkX2V4Y2VsKCJzdGF0MjAyMV9zbS9TbGVlcC9TTEVFUF9zaG9ydG5hbWUueGxzIiwgKQpkZiA8LSBkZiB8PiBtdXRhdGUoU0xFRVAgPSBpZmVsc2UoU0xFRVAgPCAwLCBOQSwgU0xFRVApLCBQQVJBRE9YID0gaWZlbHNlKGlzLmluZmluaXRlKFBBUkFET1gpLCBOQSwgUEFSQURPWCksIFBSRURfSU5EID0gYXMuZmFjdG9yKFBSRURfSU5EKSwgRVhQX0lORCA9IGFzLmZhY3RvcihFWFBfSU5EKSwgREFOR19JTkQgPSBhcy5mYWN0b3IoREFOR19JTkQpKSAKZGYgfD4gc2xpY2UoMToxMCkgfD4gcHJpbnRfZGYoKQpgYGAKCiMjIDIuINCe0L/QuNGB0LDQvdC40LUg0L/RgNC40LfQvdCw0LrQvtCyCgoxLiAqKipOQU1FKioqIC0tLSDQvdCw0LfQstCw0L3QuNC1INC20LjQstC+0YLQvdC+0LPQviAtLS0gKirQutCw0YfQtdGB0YLQstC10L3QvdGL0Lkg0L/RgNC40LfQvdCw0LoqKgoyLiAqKipCT0RZX1dFSSoqKiAtLS0g0LLQtdGBINGC0LXQu9CwINCyINC60LjQu9C+0LPRgNCw0LzQvNCw0YUgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90Ysg0L3QtdC/0YDQtdGA0YvQstC90YvQuSDQv9GA0LjQt9C90LDQuioqCjMuICoqKkJSQUlOX1dFCSoqKiAtLS0g0LLQtdGBINC80L7Qt9Cz0LAg0LIg0LPRgNCw0LzQvNCw0YUgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90YvQuSDQvdC10L/RgNC10YDRi9Cy0L3Ri9C5INC/0YDQuNC30L3QsNC6KioKNC4gKioqU0xPV1dBVkUqKiogLS0tINCy0YDQtdC80Y8g0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwLCDRh9Cw0YHQvtCyINCyINC00LXQvdGMIC0tLSAqKtC60L7Qu9C40YfQtdGB0YLQstC10L3QvdGL0Lkg0L3QtdC/0YDQtdGA0YvQstC90YvQuSDQv9GA0LjQt9C90LDQuioqCjUuICoqKlBBUkFET1gqKiogLS0t0LLRgNC10LzRjyDQsdGL0YHRgtGA0L7Qs9C+INGB0L3QsCwg0YfQsNGB0L7QsiDQsiDQtNC10L3RjCAgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90YvQuSDQvdC10L/RgNC10YDRi9Cy0L3Ri9C5INC/0YDQuNC30L3QsNC6KioKNi4gKioqU0xFRVAqKiogLS0tINC+0LHRidC10LUg0LLRgNC10LzRjyDRgdC90LAsINGH0LDRgdC+0LIg0LIg0LTQtdC90YwgLS0tICoq0LrQvtC70LjRh9C10YHRgtCy0LXQvdC90YvQuSDQvdC10L/RgNC10YDRi9Cy0L3Ri9C5INC/0YDQuNC30L3QsNC6KioKNy4gKioqTElGRVNQQU4qKiogLS0tINC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCDQsiDQs9C+0LTQsNGFIC0tLSAqKtC60L7Qu9C40YfQtdGB0YLQstC10L3QvdGL0Lkg0L3QtdC/0YDQtdGA0YvQstC90YvQuSDQv9GA0LjQt9C90LDQuioqCjguICoqKkdFU1RUSU1FKioqIC0tLSDQstGA0LXQvNGPINCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCDQsiDQtNC90Y/RhSAtLS0gKirQutC+0LvQuNGH0LXRgdGC0LLQtdC90L3Ri9C5INC90LXQv9GA0LXRgNGL0LLQvdGL0Lkg0L/RgNC40LfQvdCw0LoqKgo5LiAqKipQUkVEX0lORCoqKiAtLS0g0LjQvdC00LXQutGBINGF0LjRidC90LjRh9C10YHRgtCy0LAgKDEgLS0tINC90LDQuNC80LXQvdC10LUg0LLQtdGA0L7Rj9GC0L3Ri9C5INC+0LHRitC10LrRgiDQtNC70Y8g0L7RhdC+0YLRiywgNSAtLS0g0L3QsNC40LHQvtC70LXQtSDQstC10YDQvtGP0YLQvdGL0Lkg0L7QsdGK0LXQutGCINC00LvRjyDQvtGF0L7RgtGLKSAtLS0gKirQv9C+0YDRj9C00LrQvtCy0YvQuSDQv9GA0LjQt9C90LDQuioqCjEwLiAqKipFWFBfSU5EKioqIC0tLSDQuNC90LTQtdC60YEg0LLQvtC30LTQtdC50YHRgtCy0LjRjyDQstC+INCy0YDQtdC80Y8g0YHQvdCwICgxIC0tLSDQvdCw0LjQvNC10L3QtdC1INC/0L7QtNCy0LXRgNC20LXQvdC90YvQuSDQstC+0LfQtNC10LnRgdGC0LLQuNGOICjQvdCw0L/RgNC40LzQtdGALCDQttC40LLQvtGC0L3QvtC1INGB0L/QuNGCINCyCtGF0L7RgNC+0YjQviDQt9Cw0YnQuNGJ0LXQvdC90L7QvCDQu9C+0LPQvtCy0LUpLCA1IC0tLSDQvdCw0LjQsdC+0LvQtdC1INC/0L7QtNCy0LXRgNC20LXQvdC90YvQuSDQstC+0LfQtNC10LnRgdGC0LLQuNGOKSAtLS0gKirQv9C+0YDRj9C00LrQvtCy0YvQuSDQv9GA0LjQt9C90LDQuioqCjExLiAqKipEQU5HX0lORCoqKiAtLS0g0L7QsdGJ0LjQuSDQuNC90LTQtdC60YEg0L7Qv9Cw0YHQvdC+0YHRgtC4ICjQvdCwINC+0YHQvdC+0LLQtSDQtNCy0YPRhSDQstGL0YjQtdGD0LrQsNC30LDQvdC90YvRhSDQuNC90LTQtdC60YHQvtCyINC4INC00YDRg9Cz0L7QuSDQuNC90YTQvtGA0LzQsNGG0LjQuCkgKDEgLS0tINC90LDQuNC80LXQvdGM0YjQsNGPINC+0L/QsNGB0L3QvtGB0YLRjCAo0L7RgiDQtNGA0YPQs9C40YUg0LbQuNCy0L7RgtC90YvRhSksIDUgLS0tINC90LDQuNCx0L7Qu9GM0YjQsNGPINC+0L/QsNGB0L3QvtGB0YLRjCAo0YHQviDRgdGC0L7RgNC+0L3RiyDQtNGA0YPQs9C40YUg0LbQuNCy0L7RgtC90YvRhSkpIC0tLSAqKtC/0L7RgNGP0LTQutC+0LLRi9C5INC/0YDQuNC30L3QsNC6KioKCiMjIDMuINCc0LDRgtGA0LjRh9C90YvQuSDQs9GA0LDRhNC40Log0YDQsNC30LHRgNC+0YHQsAoKYGBge3J9CmNhdGVnb3JpZXMgPC1saXN0KE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIE5VTEwsIDE6NSwgMTo1LCAxOjUpCmRmIHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IHNjYXR0ZXJQbG90TWF0cml4KHJlZ3Jlc3Npb25UeXBlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgY29yclBsb3RUeXBlID0gIlRleHQiLAogICAgICAgICAgICAgICAgICAgICAgICBzbGlkZXJzUG9zaXRpb24gPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgIGRpbUNvdW50ID0gNywKICAgICAgICAgICAgICAgICAgICAgICAgICB4U3RhcnRpbmdEaW1JbmRleCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeVN0YXJ0aW5nRGltSW5kZXggPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsID0gY2F0ZWdvcmllcywKICAgICAgICAgICAgICAgICAgICAgICAgcGxvdFByb3BlcnRpZXMgPSBsaXN0KG5vQ2F0Q29sb3IgPSAiSW5kaWdvIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2xXaWRnZXRzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gMTA1MCwgd2lkdGggPSAxMDAwKQpgYGAKCtCS0LjQtNC40Lwg0LTQvtGB0YLQsNGC0L7Rh9C90L4g0LvQvtCz0LjRh9C90YvQtSDQutCw0L3QtNC40LTQsNGC0Ysg0L3QsCDQu9C+0LPQsNGA0LjRhNC80LjRgNC+0LLQsNC90LjQtTogKirQstC10YEg0YLQtdC70LAqKiwgKirQstC10YEg0LzQvtC30LPQsCoqLCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCoqINC4ICoq0LLRgNC10LzRjyDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiAtLS0g0Y3RgtC+INC80YPQu9GM0YLQuNC/0LvQtdC60LDRgtC40LLQvdGL0LUg0L/RgNC40LfQvdCw0LrQuC4g0KLQsNC60LbQtSAqKtCy0YDQtdC80Y8g0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDQuNC80LXQtdGCINC/0LXRgNC10LrQvtGBINCy0LvQtdCy0L4sINGC0LDQuiDRh9GC0L4g0LvRg9GH0YjQtSDQtdCz0L4g0YLQvtC20LUg0L7RgtC70L7Qs9Cw0YDQuNGE0LzQuNGA0L7QstCw0YLRjCwg0YXQvtGC0Y8g0LvQvtCz0LjRh9C10YHQutC4LCDRgtCw0Log0LrQsNC6ICoq0LLRgNC10LzRjyDRgdC90LAqKiDRgdC+0LTQtdGA0LbQuNGCINCyINGB0LXQsdC1ICoq0LLRgNC10LzRjyDQsdGL0YHRgtGA0L7Qs9C+INGB0L3QsCoqLCDRjdGC0L7RgiDQv9GA0LjQt9C90LDQuiDQvdC1INGP0LLQu9GP0LXRgtGB0Y8g0LzRg9C70YzRgtC40L/Qu9C10LrQsNGC0LjQstC90YvQvCwg0LAg0YLQsNC60LbQtSDQvdC1INC40LzQtdC10YIg0L3QtdC70LjQvdC10LnQvdGD0Y4g0LfQsNCy0LjRgdC40LzQvtGB0YLRjC4g0J7QtNC90LDQutC+LCDQsNGB0YHQuNC80LXRgtGA0LjRjyAqKtCy0YDQtdC80Lgg0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDQuNC80LXQtdGCINC00L7RgdGC0LDRgtC+0YfQvdC+INCx0L7Qu9GM0YjQvtC1INC30L3QsNGH0LXQvdC40LU6CgpgYGB7cn0KZGVzYzAgPC0gZGYgfD5kcGx5cjo6c2VsZWN0KC1OQU1FLCAtUFJFRF9JTkQsIC1FWFBfSU5ELCAtREFOR19JTkQpIHw+IGRlc2NyaWJlKCkgfD5kcGx5cjo6c2VsZWN0KC10cmltbWVkLCAtbWFkLCAtc2UpCnByaW50X2RmKGRlc2MwKQpgYGAKCiMjIDQuINCb0L7Qs9Cw0YDQuNGE0LzQuNGA0L7QstCw0L3QuNC1INGF0LLQvtGB0YLQsNGC0YvRhQoKYGBge3J9CmRmLmxvZyA8LSBkZgpkZi5sb2cgPC0gZGYubG9nIHw+IG11dGF0ZShCT0RZX1dFSSA9IGxvZyhCT0RZX1dFSSksIEJSQUlOX1dFID0gbG9nKEJSQUlOX1dFKSwgUEFSQURPWCA9IGxvZyhQQVJBRE9YKSwgTElGRVNQQU4gPSBsb2coTElGRVNQQU4pLCBHRVNUVElNRSA9IGxvZyhHRVNUVElNRSkpCmRmLmxvZyA8LSBkZi5sb2cgfD4gbXV0YXRlKFBBUkFET1ggPSBpZmVsc2UoaXMuaW5maW5pdGUoUEFSQURPWCksIE5BLCBQQVJBRE9YKSkKZGYubG9nIHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IGZpbHRlcighaXMubmEoU0xPV1dBVkUpICYgIWlzLm5hKFBBUkFET1gpICYgIWlzLm5hKFNMRUVQKSAmICFpcy5uYShMSUZFU1BBTikgJiAhaXMubmEoR0VTVFRJTUUpKSB8PiBzY2F0dGVyUGxvdE1hdHJpeChyZWdyZXNzaW9uVHlwZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJQbG90VHlwZSA9ICJUZXh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgc2xpZGVyc1Bvc2l0aW9uID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1Db3VudCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeFN0YXJ0aW5nRGltSW5kZXggPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgIHlTdGFydGluZ0RpbUluZGV4ID0gMQogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbCA9IGNhdGVnb3JpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RQcm9wZXJ0aWVzID0gbGlzdChub0NhdENvbG9yID0gIkluZGlnbyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sV2lkZ2V0cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDEwNTAsIHdpZHRoID0gMTAwMCkKYGBgCgrQktGL0LLQtdC00LXQvCDRgtCw0LrQttC1INC90LXQvNC90L7Qs9C+INC00YDRg9Cz0L7QuSDQvNCw0YLRgNC40YfQvdGL0Lkg0LPRgNCw0YTQuNC6LCDQsiDQutC+0YLQvtGA0L7QvCDRg9C00LDQu9C10L3QuNC1IE5BINCx0YPQtNC10YIg0L/QvtC/0LDRgNC90YvQvC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpkZi5sb2cgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSkgfD4gZ2dwYWlycyhjb2x1bW5zID0gMTo3KQpgYGAKCtCS0LjQtNC40LwsINGH0YLQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjyDRgdGC0LDQu9C4INCx0L7Qu9C10LUg0YHQuNC80LzQtdGC0YDQuNGH0L3Ri9C80LgsINC/0YDQuNGH0ZHQvCDQv9C+0YfRgtC4INCy0YHQtSDQv9GA0LjQt9C90LDQutC4INC40LzQtdGO0YIg0L7RgtGA0LjRhtCw0YLQtdC70YzQvdGL0Lkg0Y3QutGB0YbQtdGB0YEsINGC0L4g0LXRgdGC0Ywg0L7QvdC4INCx0L7Qu9C10LUgItC/0LvQvtGB0LrQuNC1Iiwg0YfQtdC8INC90L7RgNC80LDQu9GM0L3QvtC1OgoKYGBge3J9CmRlc2MxIDwtIGRmLmxvZyB8PmRwbHlyOjpzZWxlY3QoLU5BTUUsIC1QUkVEX0lORCwgLUVYUF9JTkQsIC1EQU5HX0lORCkgfD4gZGVzY3JpYmUoKSB8PmRwbHlyOjpzZWxlY3QoLXRyaW1tZWQsIC1tYWQsIC1zZSkKcHJpbnRfZGYoZGVzYzEpCmBgYAoK0J/QviDQs9GA0LDRhNC40LrRgyDQvdC10YDQsNCy0L3QvtC80LXRgNC90L7RgdGC0LXQuSDQvdC1INCy0LjQtNC90L4uINCV0YHQu9C4INGA0LDRgdC60YDQsNGB0LjRgtGMINC/0L4g0LrQsNGC0LXQs9C+0YDQuNCw0LvRjNC90YvQvCDQv9GA0LjQt9C90LDQutCw0LwsINGC0L4g0YLQvtC20LUg0L3QtSDQv9C+0LvRg9GH0LDQtdGC0YHRjyDQstGL0LTQtdC70LjRgtGMINGA0LDQt9C00LXQu9GM0L3Ri9C1ICrQvtCx0LvQsNC60LAqLgoKIyMgNS4g0JLRi9Cx0YDQvtGB0YsKCtCS0LjQtNC40Lwg0L/QsNGA0YMg0LLRi9Cx0YDQvtGB0L7Qsiwg0LTQu9GPINC60L7RgtC+0YDRi9GFINCy0LXRgSDQvNC+0LfQs9CwINC80LDQuywg0L3QviDQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtGMINC20LjQt9C90Lgg0LTQvtGB0YLQsNGC0L7Rh9C90L4g0LLQtdC70LjQutCwOgoKYGBge3J9CmRmLmxvZyB8PiBmaWx0ZXIoTElGRVNQQU4gPiAyICYgQlJBSU5fV0UgPCAwKSB8PiBwcmludF9kZigpCmBgYAoK0K3RgtC+INC00LLQtSAqKtC70LXRgtGD0YfQuNC1INC80YvRiNC4KiouCgrQn9C+0YHRgtGA0L7QuNC8INGC0LXQv9C10YDRjCDQs9GA0LDRhNC40LrQuCDRgNCw0LfQsdGA0L7RgdCwINCx0LXQtyDQstGL0LHRgNC+0YHQvtCyLgoKYGBge3J9CmRmLmxvZy5vdXQgPC0gZGYubG9nIHw+ICBmaWx0ZXIoIShMSUZFU1BBTiA+IDIgJiBCUkFJTl9XRSA8IDApKQpkZi5sb2cub3V0IHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IGZpbHRlcighaXMubmEoU0xPV1dBVkUpICYgIWlzLm5hKFBBUkFET1gpICYgIWlzLm5hKFNMRUVQKSAmICFpcy5uYShMSUZFU1BBTikgJiAhaXMubmEoR0VTVFRJTUUpKSB8PiBzY2F0dGVyUGxvdE1hdHJpeChyZWdyZXNzaW9uVHlwZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJQbG90VHlwZSA9ICJUZXh0IiwKICAgICAgICAgICAgICAgICAgICAgICAgc2xpZGVyc1Bvc2l0aW9uID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1Db3VudCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeFN0YXJ0aW5nRGltSW5kZXggPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgIHlTdGFydGluZ0RpbUluZGV4ID0gMQogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbCA9IGNhdGVnb3JpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RQcm9wZXJ0aWVzID0gbGlzdChub0NhdENvbG9yID0gIkluZGlnbyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sV2lkZ2V0cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDEwNTAsIHdpZHRoID0gMTAwMCkKYGBgCgrQmCDRgdGA0LDQstC90LjQvCDQutC+0YDRgNC10LvRj9GG0LjQuC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpkZi5sb2cgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSkgfD4gZ2dwYWlycyhjb2x1bW5zID0gMTo3KQpkZi5sb2cub3V0IHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpIHw+IGdncGFpcnMoY29sdW1ucyA9IDE6NykKYGBgCgrQkiDQvtGB0L3QvtCy0L3QvtC8INC+0L3QuCAqKtCy0YvRgNC+0YHQu9C4KiosINC/0L4g0LrRgNCw0LnQvdC10Lkg0LzQtdGA0LUsINGC0LDQvCwg0LPQtNC1INC80Ysg0LjRgdC60LvRjtGH0LDQu9C4INCy0YvQsdGA0L7RgdGLLgoKIyMgNi4g0JfQsNCy0LjRgdC40LzQvtGB0YLQuCDQvtGCINC40L3QtNC10LrRgdC+0LIg0L7Qv9Cw0YHQvdC+0YHRgtC4CgrQndC10L7QsdGF0L7QtNC40LzQviDQvtC/0LjRgdCw0YLRjCDRgNCw0LfQvdC40YbRgyDQvNC10LbQtNGDINC20LjQstC+0YLQvdGL0LzQuCDQv9C+INC40L3QtNC10LrRgdGDINC+0L/QsNGB0L3QvtGB0YLQuCDQvNC10YHRgtCwLCDQs9C00LUg0L7QvdC4INGB0L/Rj9GCLiDQn9C+0YHQvNC+0YLRgNC40Lwg0L3QsCDQutC+0LvQuNGH0LXRgdGC0LLQviDQttC40LLQvtGC0L3Ri9GFINCyINC60LDQttC00L7QuSDQs9GA0YPQv9C/0LU6CgpgYGB7cn0KZGYubG9nLm91dCB8PmRwbHlyOjpzZWxlY3QoRVhQX0lORCkgfD4gc3VtbWFyeSgpCmBgYAoK0JLRi9Cx0L7RgNC60Lgg0LTQu9GPINC40L3QtNC10LrRgdC+0LIgJDIkINC4ICQ1JCDQvNC+0LbQtdC8INGB0YfQuNGC0LDRgtGMINGB0LHQsNC70LDQvdGB0LjRgNC+0LLQsNC90L3Ri9C80LguCgrQktC40LfRg9Cw0LvRjNC90L4g0L7RhtC10L3QuNC8INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPINGBINC/0L7QvNC+0YnRjNGOINGP0YnQuNC60L7QsiDRgSDRg9GB0LDQvNC4OgoKYGBge3J9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IEJPRFlfV0VJKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IEJSQUlOX1dFKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYubG9nLm91dCB8PiBnZ3Bsb3QoYWVzKHggPSBFWFBfSU5ELCB5ID0gUEFSQURPWCkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IFNMT1dXQVZFKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CmRmLmxvZy5vdXQgfD4gZ2dwbG90KGFlcyh4ID0gRVhQX0lORCwgeSA9IFNMRUVQKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYubG9nLm91dCB8PiBnZ3Bsb3QoYWVzKHggPSBFWFBfSU5ELCB5ID0gTElGRVNQQU4pKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkZi5sb2cub3V0IHw+IGdncGxvdChhZXMoeCA9IEVYUF9JTkQsIHkgPSBHRVNUVElNRSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCtCSINC+0YHQvdC+0LLQvdC+0Lwg0LLQuNC00L3QsCDQvNC+0L3QvtGC0L7QvdC90LDRjyDQt9Cw0LLQuNGB0LjQvNC+0YHRgtGMLCDQvtC00L3QsNC60L4sINC/0YDQtdC00YHRgtC+0LjRgiDQv9GA0L7QstC10YDQuNGC0Ywg0Y3RgtC+INGBINC/0L7QvNC+0YnRjNGOINC60YDQuNGC0LXRgNC40LXQsi4KCiMjIDcuINCd0L7RgNC80LDQu9GM0L3QvtGB0YLRjCwg0LLQuNC0INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPCgrQmNC3LdC30LAg0LzQsNC70L7RgdGC0Lgg0LLRi9Cx0L7RgNC+0Log0LIg0YLRgNC10YLRjNC10Lkg0Lgg0YfQtdGC0LLRkdGA0YLQvtC5INCz0YDRg9C/0L/QsNGFINC40YHQutC70Y7Rh9C40Lwg0LjRhSDQuNC3INC40YHRgdC70LXQtNC+0LLQsNC90LjRjy4KCmBgYHtyfQpnZXRfZGZfb25faW5kIDwtIGZ1bmN0aW9uKGkpIHsKICByZXR1cm4oZGYubG9nLm91dCB8PiBmaWx0ZXIoRVhQX0lORCA9PSBpKSkKfQpgYGAKCtCf0L7RgdC80L7RgtGA0LjQvCDRgdC90LDRh9Cw0LvQsCDQvdCwINCz0LjRgdGC0L7Qs9GA0LDQvNC80Ysg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Lkg0L/QviDQs9GA0YPQv9C/0LDQvCDQtNC70Y8g0LrQsNC20LTQvtCz0L4g0L/RgNC40LfQvdCw0LrQsC4KCmBgYHtyfQpmb3IgKGkgaW4gYygxLCAyLCA1KSkgewogIHByaW50KHBhc3RlKCLQk9GA0YPQv9C/0LAgIiwgaSkpCiAgcGFyKG1mcm93ID0gYygyLCA0KSkKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkQk9EWV9XRUksIG1haW4gPSAiIikKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIG1haW4gPSAiIikKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkU0xPV1dBVkUsIG1haW4gPSAiIikKICBoaXN0KGdldF9kZl9vbl9pbmQoaSkkUEFSQURPWCwgbWFpbiA9ICIiKQogIGhpc3QoZ2V0X2RmX29uX2luZChpKSRTTEVFUCwgbWFpbiA9ICIiKQogIGhpc3QoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgbWFpbiA9ICIiKQogIGhpc3QoZ2V0X2RmX29uX2luZChpKSRHRVNUVElNRSwgbWFpbiA9ICIiKQp9CmBgYAoK0J/QvtGB0LzQvtGC0YDQuNC8INC90LAg0L3QvtGA0LzQsNC70YzQvdC+0YHRgtGMINGBINC/0L7QvNC+0YnRjNGOIE5vcm1hbCBQcm9iYWJpbGl0eSBQbG90OgoKYGBge3J9CmZvciAoaSBpbiBjKDEsIDIsIDUpKSB7CiAgcHJpbnQocGFzdGUoItCT0YDRg9C/0L/QsCAiLCBpKSkKICBwYXIobWZyb3cgPSBjKDIsIDQpKQogIHBsb3RfcXFfZ3JhcGgoZ2V0X2RmX29uX2luZChpKSRCT0RZX1dFSSwgIkJPRFlfV0VJIikKICBwbG90X3FxX2dyYXBoKGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsICJCUkFJTl9XRSIpCiAgcGxvdF9xcV9ncmFwaChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFLCAiU0xPV1dBVkUiKQogIHBsb3RfcXFfZ3JhcGgoZ2V0X2RmX29uX2luZChpKSRQQVJBRE9YLCAiUEFSQURPWCIpCiAgcGxvdF9xcV9ncmFwaChnZXRfZGZfb25faW5kKGkpJFNMRUVQLCAiU0xFRVAiKQogIHBsb3RfcXFfZ3JhcGgoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgIkxJRkVTUEFOIikKICBwbG90X3FxX2dyYXBoKGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsICJHRVNUVElNRSIpCn0KYGBgCgrQmCDRgSDQv9C+0LzQvtGJ0YzRjiBQUC1wbG90OgoKYGBge3J9CmZvciAoaSBpbiBjKDEsIDIsIDUpKSB7CiAgcHJpbnQocGFzdGUoItCT0YDRg9C/0L/QsCAiLCBpKSkKICBwYXIobWZyb3cgPSBjKDIsIDQpKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKEJPRFlfV0VJKSkpJEJPRFlfV0VJLCAiQk9EWV9XRUkiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKEJSQUlOX1dFKSkpJEJSQUlOX1dFLCAiQlJBSU5fV0UiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKFNMT1dXQVZFKSkpJFNMT1dXQVZFLCAiU0xPV1dBVkUiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKFBBUkFET1gpKSkkUEFSQURPWCwgIlBBUkFET1giKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKFNMRUVQKSkpJFNMRUVQLCAiU0xFRVAiKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKExJRkVTUEFOKSkpJExJRkVTUEFOLCAiTElGRVNQQU4iKQogIHBsb3RfcHBfcGxvdCgoZ2V0X2RmX29uX2luZChpKSB8PiBmaWx0ZXIoIWlzLm5hKEdFU1RUSU1FKSkpJEdFU1RUSU1FLCAiR0VTVFRJTUUiKQp9CmBgYAoK0JAg0YLQtdC/0LXRgNGMINC+0YbQtdC90LjQvCDQv9C+INGC0LXRgdGC0YMg0KjQsNC/0LjRgNC+LdCj0LjQu9C60LA6CgpgYGB7cn0KZGYuc2hhcGlyby50ZXN0LnAudmFsdWUgPC0gZGF0YS5mcmFtZShpbmQgPSBjKDEsIDIsIDUpLCBCT0RZX1dFSV9QID0gcmVwKDAsIDMpLCBCUkFJTl9XRV9QID0gcmVwKDAsIDMpLCBTTE9XV0FWRV9QID0gcmVwKDAsIDMpLCBQQVJBRE9YX1AgPSByZXAoMCwgMyksIFNMRUVQX1AgPSByZXAoMCwgMyksIExJRkVTUEFOX1AgPSByZXAoMCwgMyksIEdFU1RUSU1FX1AgPSByZXAoMCwgMykpCgpmb3IgKGkgaW4gYygxLCAyLCA1KSkgewogIGRmLnNoYXBpcm8udGVzdC5wLnZhbHVlW2RmLnNoYXBpcm8udGVzdC5wLnZhbHVlJGluZCA9PSBpLCBdIDwtIGMoaSwgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkQk9EWV9XRUkpJHAudmFsdWUsIAogIHNoYXBpcm8udGVzdChnZXRfZGZfb25faW5kKGkpJEJSQUlOX1dFKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChnZXRfZGZfb25faW5kKGkpJFBBUkFET1gpJHAudmFsdWUsCiAgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkU0xFRVApJHAudmFsdWUsCiAgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkTElGRVNQQU4pJHAudmFsdWUsCiAgc2hhcGlyby50ZXN0KGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUpJHAudmFsdWUpCn0KCmRmLnNoYXBpcm8udGVzdC5wLnZhbHVlIHw+IHByaW50X2RmKCkKYGBgCgrQmCDQvdCw0LrQvtC90LXRhiDQv9C+INCb0LjQu9C70LjQtdGE0L7RgNGB0YM6CgpgYGB7cn0KZGYubGlsbGllLnRlc3QucC52YWx1ZSA8LSBkYXRhLmZyYW1lKGluZCA9IGMoMSwgMiwgNSksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmZvciAoaSBpbiBjKDEsIDIsIDUpKSB7CiAgZGYubGlsbGllLnRlc3QucC52YWx1ZVtkZi5saWxsaWUudGVzdC5wLnZhbHVlJGluZCA9PSBpLCBdIDwtIGMoaSwgbGlsbGllLnRlc3QoZ2V0X2RmX29uX2luZChpKSRCT0RZX1dFSSkkcC52YWx1ZSwgCiAgbGlsbGllLnRlc3QoZ2V0X2RmX29uX2luZChpKSRCUkFJTl9XRSkkcC52YWx1ZSwKICBsaWxsaWUudGVzdChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFKSRwLnZhbHVlLAogIGxpbGxpZS50ZXN0KGdldF9kZl9vbl9pbmQoaSkkUEFSQURPWCkkcC52YWx1ZSwKICBsaWxsaWUudGVzdChnZXRfZGZfb25faW5kKGkpJFNMRUVQKSRwLnZhbHVlLAogIGxpbGxpZS50ZXN0KGdldF9kZl9vbl9pbmQoaSkkTElGRVNQQU4pJHAudmFsdWUsCiAgbGlsbGllLnRlc3QoZ2V0X2RmX29uX2luZChpKSRHRVNUVElNRSkkcC52YWx1ZSkKfQoKZGYubGlsbGllLnRlc3QucC52YWx1ZSB8PiBwcmludF9kZigpCmBgYAoK0K3RgtC+0YIg0LrRgNC40YLQtdGA0LjQuSDRj9Cy0LvRj9C10YLRgdGPINC80LXQvdC10LUg0LzQvtGJ0L3Ri9C8LCDQvtC00L3QsNC60L4g0LTQsNGR0YIg0YHRhdC+0LbQuNC1INGA0LXQt9GD0LvRjNGC0LDRgtGLLgoK0J/QviDQuNGC0L7Qs9GDINC80L7QttC90L4g0YHRh9C40YLQsNGC0YwsINGH0YLQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjyDQstGB0LXRhSDQv9GA0LjQt9C90LDQutC+0LIg0LLQviDQstGB0LXRhSDQs9GA0YPQv9C/0LDRhSDQv9C+0YXQvtC20Lgg0L3QsCDQvdC+0YDQvNCw0LvRjNC90YvQtSwg0LrRgNC+0LzQtSAqKtCy0LXRgdCwINGC0LXQu9CwKiog0LggKirQstC10YHQsCDQvNC+0LfQs9CwKiog0LLQviDQstGC0L7RgNC+0Lkg0LPRgNGD0L/Qv9C1INC4ICoq0LLRgNC10LzQtdC90Lgg0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwKiosICoq0LLRgNC10LzQtdC90Lgg0YHQvdCwKiog0LggKirQstGA0LXQvNC10L3QuCDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiDQsiDQv9GP0YLQvtC5INCz0YDRg9C/0L/QtS4gCgojIyA4LiB0LdGC0LXRgdGC0YsKCtCh0L3QsNGH0LDQu9CwINC/0L7RgdC80L7RgtGA0LjQvCDQvdCwINC00LjRgdC/0LXRgNGB0LjRjiwg0YfRgtC+0LHRiyDQv9GA0LjQvNC10L3QuNGC0YwgdC10ZXN0INC00LvRjyDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjQuSDRgSDRgNCw0LLQvdGL0LzQuCDQtNC40YHQv9C10YDRgdC40Y/QvNC4LiDQotC10YHRgiDQpNC40YjQtdGA0LAg0L/RgNC+0LLQtdGA0Y/QtdGCINGA0LDQstC10L3RgdGC0LLQviDQtNC40YHQv9C10YDRgdC40Lkg0LTQu9GPINC90L7RgNC80LDQu9GM0L3QviDRgNCw0YHQv9GA0LXQtNC10LvRkdC90L3Ri9GFINC00LDQvdC90YvRhSwg0LrQvtGC0L7RgNGL0LUg0LzRiyDQvtC/0YDQtdC00LXQu9C10LvQuNC70Lgg0LIg0L/RgNC+0YjQu9C+0Lwg0L/Rg9C90LrRgtC1OgoKYGBge3J9CmRmLmZpc2hlci52YXIgPC0gZGF0YS5mcmFtZShJTkQxID0gcmVwKDAsIDMpLCBJTkQyID0gcmVwKDAsIDMpLCBCT0RZX1dFSV9QID0gcmVwKDAsIDMpLCBCUkFJTl9XRV9QID0gcmVwKDAsIDMpLCBTTE9XV0FWRV9QID0gcmVwKDAsIDMpLCBQQVJBRE9YX1AgPSByZXAoMCwgMyksIFNMRUVQX1AgPSByZXAoMCwgMyksIExJRkVTUEFOX1AgPSByZXAoMCwgMyksIEdFU1RUSU1FX1AgPSByZXAoMCwgMykpCgprIDwtIDEKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBmb3IgKGogaW4gYygxLCAyLCA1KSl7CiAgICBpZiAoaSA8IGopewogICAgICBkZi5maXNoZXIudmFyW2ssIF0gPC0gYyhpLCBqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLnRlc3QoZ2V0X2RmX29uX2luZChpKSRCT0RZX1dFSSwgZ2V0X2RmX29uX2luZChqKSRCT0RZX1dFSSkkcC52YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhci50ZXN0KGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIGdldF9kZl9vbl9pbmQoaikkQlJBSU5fV0UpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIudGVzdChnZXRfZGZfb25faW5kKGkpJFNMT1dXQVZFLCBnZXRfZGZfb25faW5kKGopJFNMT1dXQVZFKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLnRlc3QoZ2V0X2RmX29uX2luZChpKSRQQVJBRE9YLCBnZXRfZGZfb25faW5kKGopJFBBUkFET1gpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIudGVzdChnZXRfZGZfb25faW5kKGkpJFNMRUVQLCBnZXRfZGZfb25faW5kKGopJFNMRUVQKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyLnRlc3QoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgZ2V0X2RmX29uX2luZChqKSRMSUZFU1BBTikkcC52YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhci50ZXN0KGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsIGdldF9kZl9vbl9pbmQoaikkR0VTVFRJTUUpJHAudmFsdWUpCiAgICAgIGsgPC0gayArIDEKICAgIH0KICB9Cn0KCmRmLmZpc2hlci52YXIgfD4gcHJpbnRfZGYoKQpgYGAKCtCT0LjQv9C+0YLQtdC30LAg0L4g0YDQsNCy0LXQvdGB0YLQstC1INC90LUg0L7RgtCy0LXRgNCz0LDQtdGC0YHRjyDQtNC70Y8gKirQstC10YHQsCDRgtC10LvQsCoqINC4ICoq0LLQtdGB0LAg0LzQvtC30LPQsCoqINC80LXQttC00YMg0L/QtdGA0LLQvtC5INC4INC/0Y/RgtC+0Lkg0LPRgNGD0L/Qv9GLLCDQtNC70Y8gKirQstGA0LXQvNC10L3QuCDQvNC10LTQu9C10L3QvdC+0LPQviDRgdC90LAqKiwgKirQstGA0LXQvNC10L3QuCDRgdC90LAqKiDQuCAqKtCy0YDQtdC80LXQvdC4INCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqINC80LXQttC00YMg0L/QtdGA0LLQvtC5INC4INCy0YLQvtGA0L7QuSDQs9GA0YPQv9C/0YssICoq0LLRgNC10LzQtdC90Lgg0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDQvNC10LbQtNGDINCy0YHQtdC80Lgg0LPRgNGD0L/Qv9Cw0LzQuCDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Lgg0LbQuNC30L3QuCoqINC80LXQttC00YMg0L/QtdGA0LLQvtC5INC4INCy0YLQvtGA0L7QuSDQs9GA0YPQv9C/0L7QuS4KCtCf0YDQvtCy0LXQtNGR0LwgdC3RgtC10YHRgtGLINC00LvRjyDQv9C10YDQstC+0LksINCy0YLQvtGA0L7QuSDQuCDQv9GP0YLQvtC5INCz0YDRg9C/0L/RiyDQv9C+INCy0YHQtdC8INC/0YDQuNC30L3QsNC60LDQvDoKCmBgYHtyfQpkZi50LnRlc3QucC52YWx1ZSA8LSBkYXRhLmZyYW1lKElORDEgPSByZXAoMCwgMyksIElORDIgPSByZXAoMCwgMyksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmsgPC0gMQpmbGFnIDwtIEZBTFNFCmZsYWdCQiA8LSBGQUxTRQpmbGFnU1NHIDwtRkFMU0UKZmxhZ0wgPC0gRkFMU0UKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBmb3IgKGogaW4gYygxLCAyLCA1KSl7CiAgICBpZiAoaSA8IGopewogICAgICAgIGZsYWcgPC0gaSA9PSAyICYgaiA9PSA1CiAgICAgICAgZmxhZ0JCIDwtIGkgPT0gMSAmIGogPT0gNQogICAgICAgIGZsYWdTU0cgPC0gaSA9PSAxICYgaiA9PSAyCiAgICAgICAgZmxhZ0wgPC0gaSA9PSAxICYgaiA9PSAyCiAgICAgIAogICAgICBkZi50LnRlc3QucC52YWx1ZVtrLCBdIDwtIGMoaSwgaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHQudGVzdChnZXRfZGZfb25faW5kKGkpJEJPRFlfV0VJLCBnZXRfZGZfb25faW5kKGopJEJPRFlfV0VJLCB2YXIuZXF1YWwgPSBmbGFnIHwgZmxhZ0JCKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdC50ZXN0KGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIGdldF9kZl9vbl9pbmQoaikkQlJBSU5fV0UsIHZhci5lcXVhbCA9IGZsYWcgfCBmbGFnQkIpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRTTE9XV0FWRSwgZ2V0X2RmX29uX2luZChqKSRTTE9XV0FWRSwgdmFyLmVxdWFsID0gZmxhZyB8IGZsYWdTU0cpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRQQVJBRE9YLCBnZXRfZGZfb25faW5kKGopJFBBUkFET1gsIHZhci5lcXVhbCA9IFRSVUUpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRTTEVFUCwgZ2V0X2RmX29uX2luZChqKSRTTEVFUCwgdmFyLmVxdWFsID0gZmxhZyB8IGZsYWdTU0cpJHAudmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0LnRlc3QoZ2V0X2RmX29uX2luZChpKSRMSUZFU1BBTiwgZ2V0X2RmX29uX2luZChqKSRMSUZFU1BBTiwgdmFyLmVxdWFsID0gZmxhZyB8IGZsYWdMKSRwLnZhbHVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdC50ZXN0KGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsIGdldF9kZl9vbl9pbmQoaikkR0VTVFRJTUUsIHZhci5lcXVhbCA9IGZsYWcgfCBmbGFnU1NHKSRwLnZhbHVlKQogICAgICBrIDwtIGsgKyAxCiAgICB9CiAgfQp9CgpkZi50LnRlc3QucC52YWx1ZSB8PiBwcmludF9kZigpCmBgYAoK0JPQuNC/0L7RgtC10LfQsCDQviDRgNCw0LLQtdC90YHRgtCy0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Lkg0L/QviDQstGB0LXQvCDQv9GA0LjQt9C90LDQutCw0Lwg0LzQtdC20LTRgyDQv9C10YDQstC+0Lkg0Lgg0LLRgtC+0YDQvtC5INC90LUg0L7RgtCy0LXRgNCz0LDQtdGC0YHRjywg0LAg0LzQtdC20LTRgyDQv9C10YDQstC+0Lkg0Lgg0L/Rj9GC0L7QuSDQuCDQstGC0L7RgNC+0Lkg0Lgg0L/Rj9GC0L7QuSDQvtGC0LLQtdGA0LPQsNC10YLRgdGPLiDQntC00L3QsNC60L4g0LTQu9GPINC/0YDQuNC30L3QsNC60L7Qsiwg0LrQvtGC0L7RgNGL0LUg0LzRiyDQv9C+0YHRh9C40YLQsNC70Lgg0L3QtdC90L7RgNC80LDQu9GM0L3Ri9C80LggKCoq0LLQtdGBINGC0LXQu9CwKiog0LggKirQstC10YEg0LzQvtC30LPQsCoqINCy0L4g0LLRgtC+0YDQvtC5INCz0YDRg9C/0L/QtSDQuCAqKtCy0YDQtdC80Y8g0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwKiosICoq0LLRgNC10LzRjyDRgdC90LAqKiDQuCAqKtCy0YDQtdC80Y8g0LHQtdGA0LXQvNC10L3QvdC+0YHRgtC4Kiog0LIg0L/Rj9GC0L7QuSDQs9GA0YPQv9C/0LUpIHAtdmFsdWUg0L3QtdC70YzQt9GPINGB0YfQuNGC0LDRgtGMINCy0LXRgNC90YvQvCwg0YLQsNC6INC60LDQuiDRgNCw0LfQvNC10YDRiyDQstGL0LHQvtGA0L7QuiDQvNCw0LvRiyDQuCDRjdGC0Lgg0L/RgNC40LfQvdCw0LrQuCDQvdC1INC40LzQtdGO0YIg0L3QvtGA0LzQsNC70YzQvdC+0LPQviDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjy4KCiMjIDkuINCi0LXRgdGCINCc0LDQvdC90LAt0KPQuNGC0L3QuAoK0JTQu9GPINC/0YDQvtCy0LXRgNC60Lgg0YDQsNCy0LXQvdGB0YLQstCwINGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC5INC00LvRjyDQvdC10L3QvtGA0LzQsNC70YzQvdC+INGA0LDRgdC/0YDQtdC00LXQu9GR0L3QvdGL0YUg0L/RgNC40LfQvdCw0LrQvtCyINC80L7QttC10Lwg0L/RgNC40LzQtdC90LjRgtGMINC60YDQuNGC0LXRgNC40Lkg0JzQsNC90L3QsC3Qo9C40YLQvdC4LCDQvtC00L3QsNC60L4g0L/RgNC10LbQtNC1INC/0L7RgdC80L7RgtGA0LjQvCDQvdCwINGB0LjQvNC80LXRgtGA0LjRh9C90L7RgdGCINC/0L4g0LPRgNGD0L/Qv9Cw0Lw6CgpgYGB7cn0KZGYuc2tldyA8LSBkYXRhLmZyYW1lKElORCA9IGMoMSwgMiwgNSksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBkZi5za2V3W2RmLnNrZXckSU5EID09IGksIF0gPC0gYyhpLCBza2V3bmVzcyhnZXRfZGZfb25faW5kKGkpJEJPRFlfV0VJLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNrZXduZXNzKGdldF9kZl9vbl9pbmQoaSkkQlJBSU5fV0UsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tld25lc3MoZ2V0X2RmX29uX2luZChpKSRTTE9XV0FWRSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2V3bmVzcyhnZXRfZGZfb25faW5kKGkpJFBBUkFET1gsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tld25lc3MoZ2V0X2RmX29uX2luZChpKSRTTEVFUCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2V3bmVzcyhnZXRfZGZfb25faW5kKGkpJExJRkVTUEFOLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNrZXduZXNzKGdldF9kZl9vbl9pbmQoaSkkR0VTVFRJTUUsIG5hLnJtID0gVFJVRSkpCn0KCmRmLnNrZXcgfD4gcHJpbnRfZGYoKQpgYGAKCtCS0LjQtNC40LwsINGH0YLQviDQsNGB0LjQvNC80LXRgtGA0LjRjyAqKtCy0LXRgdCwINGC0LXQu9CwKiog0LggKirQstC10YHQsCDQvNC+0LfQs9CwKiog0YMg0LLRgtC+0YDQvtC5INCz0YDRg9C/0L/RiyAo0L3QtdC90L7RgNC80LDQu9GM0L3QviDRgNCw0YHQv9GA0LXQtNC10LvRkdC90L3Ri9C1INC/0YDQuNC30L3QsNC60LgpINGB0LjQu9GM0L3QviAo0L/RgNC40LzQtdGA0L3QviDQvdCwINC/0L7RgNGP0LTQvtC6KSDQvtGC0LvQuNGH0LDQtdGC0YHRjyDQvtGCINC/0LXRgNCy0L7QuSDQuCDQv9GP0YLQvtC5INCz0YDRg9C/0L/Riy4g0JDQvdCw0LvQvtCz0LjRh9C90L46INCw0YHQuNC80LzQtdGC0YDQuNGPICoq0LLRgNC10LzQtdC90Lgg0YHQvdCwKiog0LggKirQstGA0LXQvNC10L3QuCDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiDQv9GP0YLQvtC5INCz0YDRg9C/0L/RiyDRgdC40LvRjNC90L4g0L7RgtC70LjRh9Cw0LXRgtGB0Y8g0L7RgiDQv9C10YDQstC+0Lkg0Lgg0L/Rj9GC0L7QuS4g0JTQu9GPINCw0YHQuNC80LzQtdGC0YDQuNC4ICoq0LLRgNC10LzQtdC90Lgg0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiDRgyDQv9GP0YLQvtC5INCz0YDRg9C/0L/RiyDQuCDQv9C10YDQstC+0Lkg0YLQsNC60LbQtSDQvdC10YIg0L7RgdC90L7QstCw0L3QuNC5INCz0L7QstC+0YDQuNGC0Ywg0L4g0L/RgNC40LHQu9C40LfQuNGC0LXQu9GM0L3QvtC8INGA0LDQstC10L3RgdGC0LLQtSwg0L7QtNC90LDQutC+INC80LXQttC00YMg0L/Rj9GC0L7QuSDQuCDQstGC0L7RgNC+0Lkg0LPRgNGD0L/Qv9C+0Lkg0YDQsNC30L3QuNGG0LAg0LzQsNC70LAsINC80LXQvdGM0YjQtSAkMC41JCwg0YfRgtC+INC80L7QttC10YIg0LPQvtCy0L7RgNC40YLRjCDQvtCxINGD0LTQvtCy0LvQtdGC0LLQvtGA0LXQvdC40Lgg0YPRgdC70L7QstC40Lkg0LTQu9GPINC60YDQuNGC0LXRgNC40Y8sINC+0LTQvdCw0LrQviDQvdCw0LTQviDQtdGJ0ZEg0L/QvtGB0LzQvtGC0YDQtdGC0Ywg0L3QsCDQuNGB0L/RgNCw0LLQu9C10L3QvdGD0Y4g0LLRi9Cx0L7RgNC+0YfQvdGD0Y4g0LTQuNGB0L/QtdGA0YHQuNGOOgoKYGBge3J9CmRmLnZhciA8LSBkYXRhLmZyYW1lKElORCA9IGMoMSwgMiwgNSksIEJPRFlfV0VJX1AgPSByZXAoMCwgMyksIEJSQUlOX1dFX1AgPSByZXAoMCwgMyksIFNMT1dXQVZFX1AgPSByZXAoMCwgMyksIFBBUkFET1hfUCA9IHJlcCgwLCAzKSwgU0xFRVBfUCA9IHJlcCgwLCAzKSwgTElGRVNQQU5fUCA9IHJlcCgwLCAzKSwgR0VTVFRJTUVfUCA9IHJlcCgwLCAzKSkKCmZvciAoaSBpbiBjKDEsIDIsIDUpKXsKICBkZi52YXJbZGYudmFyJElORCA9PSBpLCBdIDwtIGMoaSwgdmFyKGdldF9kZl9vbl9pbmQoaSkkQk9EWV9XRUksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcihnZXRfZGZfb25faW5kKGkpJEJSQUlOX1dFLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIoZ2V0X2RmX29uX2luZChpKSRTTE9XV0FWRSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyKGdldF9kZl9vbl9pbmQoaSkkUEFSQURPWCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyKGdldF9kZl9vbl9pbmQoaSkkU0xFRVAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcihnZXRfZGZfb25faW5kKGkpJExJRkVTUEFOLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIoZ2V0X2RmX29uX2luZChpKSRHRVNUVElNRSwgbmEucm0gPSBUUlVFKSkKfQoKZGYudmFyIHw+IHByaW50X2RmKCkKYGBgCgrQktC40LTQuNC8LCDRh9GC0L4g0LTQuNGB0L/QtdGA0YHQuNC4ICoq0LLRgNC10LzQtdC90Lgg0LzQtdC00LvQtdC90L3QvtCz0L4g0YHQvdCwKiog0LTQu9GPINC/0Y/RgtC+0Lkg0Lgg0LLRgtC+0YDQvtC5INCz0YDRg9C/0L/RiyDQtNCw0LbQtSDQv9GA0LjQsdC70LjQt9C40YLQtdC70YzQvdC+INC90LUg0YDQsNCy0L3Riywg0L/QvtGN0YLQvtC80YMg0L3QuCDQvtC00LjQvSDQuNC3INC/0YDQuNC30L3QsNC60L7Qsiwg0LrQvtGC0L7RgNGL0LUg0LzRiyDRhdC+0YLQtdC70Lgg0L/RgNC+0LLQtdGA0LjRgtGMINC90LUg0L/QvtC00YXQvtC00Y/RgiDQtNC70Y8g0L/RgNC40LzQtdC90LXQvdC40Y8g0LrRgNC40YLQtdGA0LjRjyDQnNCw0L3QvdCwLdCj0LjRgtC90LguINCi0L4g0LXRgdGC0Ywg0LTQu9GPINC90LjRhSDQvNGLINC90LjRh9C10LPQviDQvdC1INC80L7QttC10Lwg0YHQutCw0LfQsNGC0Ywg0L4g0YDQsNCy0LXQvdGB0YLQstC1INGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNC5LgoKIyMgMTAuINCa0L7RjdGE0YTQuNGG0LjQtdC90YIg0J/QuNGA0YHQvtC90LAKCmBgYHtyfQpwYWlyd2lzZS5jb3IgPC0gZnVuY3Rpb24oZGF0YSwgbmFtZV9tZXRob2QgPSAicGVhcnNvbiIpewogIG0gPC0gY29yKGRhdGEpCiAgbS5wLnZhbHVlIDwtIGNvcihkYXRhKQogIAogIGZvciAoaSBpbiBjb2xuYW1lcyhkYXRhKSl7CiAgICBmb3IgKGogaW4gY29sbmFtZXMoZGF0YSkpewogICAgICBkYXRhLm91dCA8LSBkYXRhfD4gZmlsdGVyKCFpcy5uYShkYXRhW1tpXV0pICYgIWlzLm5hKGRhdGFbW2pdXSkpCiAgICAgIG1baSwgal0gPC0gY29yKHggPSBkYXRhLm91dFtbaV1dLCB5ID0gZGF0YS5vdXRbW2pdXSwgbWV0aG9kID0gbmFtZV9tZXRob2QpCiAgICAgIG0ucC52YWx1ZVtpLCBqXSA8LSBjb3IudGVzdCh4ID0gYXMudmVjdG9yKGRhdGEub3V0W1tpXV0pLCB5ID0gYXMudmVjdG9yKGRhdGEub3V0W1tqXV0pLCBtZXRob2QgPSBuYW1lX21ldGhvZCkkcC52YWx1ZQogICAgfQogIH0KICAKICBjb3JycGxvdChtLCBtZXRob2QgPSAibnVtYmVyIikKICBhcy5kYXRhLmZyYW1lKG0ucC52YWx1ZSkKfQpgYGAKCtCf0LXRgNC10LQg0LLRi9GH0LjRgdC70LXQvdC40LXQvCDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0LAg0LrQvtGA0YDQtdC70Y/RhtC40Lgg0J/QuNGA0YHQvtC90LAg0L/QvtGB0LzQvtGC0YDQuNC8INC90LAg0L3QvtGA0LzQsNC70YzQvdC+0YHRgtGMINC/0YDQuNC30L3QsNC60L7QsiDQtNC70Y8g0LLRgdC10YUg0LjQvdC00LjQstC40LTQvtCyINC/0L4g0YLQtdGB0YLRgyDQqNCw0L/QuNGA0L4t0KPQuNC70LrQsDoKCmBgYHtyfQpkZi5zaGFwaXJvLnRlc3QuYWxsLnAudmFsdWUgPC0gZGF0YS5mcmFtZShCT0RZX1dFSV9QID0gMCwgQlJBSU5fV0VfUCA9IDAsIFNMT1dXQVZFX1AgPSAwLCBQQVJBRE9YX1AgPSAwLCBTTEVFUF9QID0gMCwgTElGRVNQQU5fUCA9IDAsIEdFU1RUSU1FX1AgPSAwKQoKZm9yIChpIGluIGMoMSkpIHsKICBkZi5zaGFwaXJvLnRlc3QuYWxsLnAudmFsdWVbMSwgXSA8LSBjKHNoYXBpcm8udGVzdChkZi5sb2cub3V0JEJPRFlfV0VJKSRwLnZhbHVlLCAKICBzaGFwaXJvLnRlc3QoZGYubG9nLm91dCRCUkFJTl9XRSkkcC52YWx1ZSwKICBzaGFwaXJvLnRlc3QoZGYubG9nLm91dCRTTE9XV0FWRSkkcC52YWx1ZSwKICBzaGFwaXJvLnRlc3QoZGYubG9nLm91dCRQQVJBRE9YKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChkZi5sb2cub3V0JFNMRUVQKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChkZi5sb2cub3V0JExJRkVTUEFOKSRwLnZhbHVlLAogIHNoYXBpcm8udGVzdChkZi5sb2cub3V0JEdFU1RUSU1FKSRwLnZhbHVlKQp9CgpkZi5zaGFwaXJvLnRlc3QuYWxsLnAudmFsdWUgfD4gcHJpbnRfZGYoKQpgYGAKCtCa0LDQuiDQstC40LTQuNC8INC/0L4gcC12YWx1ZSDQvNC+0LbQtdC8INGB0YfQuNGC0LDRgtGMLCDRh9GC0L4g0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y8g0L/QvtGF0L7QttC4INC90LAg0L3QvtGA0LzQsNC70YzQvdGL0LUsINC/0L7RjdGC0L7QvNGDINC60YDQuNGC0LXRgNC40Lkg0L/RgNC+0LLQtdGA0LrQuCDQvdCwINC30L3QsNGH0LjQvNC+0YHRgtGMINC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQutC+0YDRgNC10LvRj9GG0LjQuCDQsdGD0LTQtdGCINC/0YDQvtCy0LXRgNGP0YLRjCDQtdGJ0ZEg0Lgg0L3QtdC30LDQstC40YHQuNC80L7RgdGC0Ywg0L/RgNC40LfQvdCw0LrQvtCyLCDQsCDRgtCw0LrQttC1INC60L7RjdGE0YTQuNGG0LjQtdC90YIg0KHQv9C40YDQvNC10L3QsCDQsdGD0LTQtdGCINC/0YDQuNC80LXRgNC90L4g0YDQsNCy0LXQvSDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0YMg0J/QuNGA0YHQvtC90LAuCgrQotC10L/QtdGA0Ywg0L/QvtGB0YfQuNGC0LDQtdC8INC60L7RjdGE0YTQuNGG0LjQtdC90YLRiyDQutC+0YDRgNC10LvRj9GG0LjQuCDQn9C40YDRgdC+0L3QsCDQtNC70Y8g0LLRgdC10YUg0L/QsNGAINC/0YDQuNC30L3QsNC60L7Qsiwg0L/RgNC40YfRkdC8INGD0LTQsNC70LXQvdC40LUgTkEg0LHRg9C00LXQvCDQv9GA0L7QuNC30LLQvtC00LjRgtGMINC/0L7Qv9Cw0YDQvdC+LCDRh9GC0L7QsdGLINC00LvRjyDQutCw0LbQtNC+0Lkg0L/QsNGA0Ysg0L/QvtC70YPRh9C40YLRjCDQvNCw0LrRgdC40LzQsNC70YzQvdC+0LUg0LrQvtC70LjRh9C10YHRgtCy0L4g0LjQvdC00LjQstC40LTQvtCyOgoKYGBge3J9CmRmLmxvZy5vdXQgfD4gCiAgbXV0YXRlKEJSQUlOX0JPRFkgPSBsb2coZXhwKEJSQUlOX1dFKSAvIGV4cChCT0RZX1dFSSkpKSB8PgogIGRwbHlyOjpzZWxlY3QoLU5BTUUsIC1FWFBfSU5ELCAtUFJFRF9JTkQsIC1EQU5HX0lORCkgfD4gCiAgcGFpcndpc2UuY29yKCkgfD4gCiAgcHJpbnRfZGYoKQpgYGAKCtCS0LjQtNC40LwsINGH0YLQviDQtNC70Y8g0LLRgdC10YUg0L/QsNGAINC/0YDQuNC30L3QsNC60L7QsiDQutGA0LjRgtC10YDQuNC5INC00LDRkdGCIHAtdmFsdWUg0LzQtdC90YzRiNC1ICRcYWxwaGEgPSAwLjA1JCwg0YLQviDQtdGB0YLRjCDQvtGC0LLQtdGA0LPQsNC10Lwg0LPQuNC/0L7RgtC10LfRgyDQviDQvdC10LfQvdCw0YfQuNC80L7RgdGC0Lgg0LfQsNCy0LjRgdC40LzRgdC+0YLQuCDQtNC70Y8g0Y3RgtC40YUg0L/RgNC40LfQvdCw0LrQvtCyLgoK0JfQsNCy0LjRgdC40LzQvtGB0YLQuDoKCjEuICoq0JLQtdGBINC80L7Qt9Cz0LAqKiDQt9Cw0LLQuNGB0LjRgiDQvtGCICoq0LLQtdGB0LAg0YLQtdC70LAqKjsKCjIuICoq0J7QsdGJ0LDRjyDQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtGMINGB0L3QsCoqINC30LDQstC40YHQuNGCINC+0YIgKirQvNC10LTQu9C10L3QvdC+0LPQvioqINC4ICoq0LHRi9GB0YLRgNC+0LPQviDRgdC90LAqKiwg0LAg0YLQsNC60LbQtSDQvtC90Lgg0LfQsNCy0LjRgdGP0YIg0LTRgNGD0LMg0L7RgiDQtNGA0YPQs9CwLCDQvdC+INGB0LrQvtGA0LXQtSDQutC+0YHQstC10L3QvdC+LCDRgtCw0Log0LrQsNC6INGN0YLQviDRgdC+0L7RgtC90L7RiNC10L3QuNC1INGB0LrQvtGA0LXQtSDQuNC90LTQuNCy0LjQtNGD0LDQu9GM0L3QviDQtNC70Y8g0LrQsNC20LTQvtCz0L4g0LbQuNCy0L7RgtC90L7Qs9C+OwoKMy4gKirQktGA0LXQvNGPINCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqINC30LDQstC40YHQuNGCINC+0YIgKirQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtC4INC20LjQt9C90LgqKiwg0YLQsNC6INC60LDQuiDQsdC10YDQtdC80LXQvdC90L7RgdGC0Ywg0L3QtSDQtNC+0LvQttC90LAg0LfQsNC90LjQvNCw0YLRjCDQsdC+0LvRjNGI0LXQuSDRh9Cw0YHRgtC4INC20LjQt9C90Lgg0LjQty3Qt9CwINGD0Y/Qt9Cy0LjQvNC+0YHRgtC4INGC0LDQutC+0LPQviDQttC40LLQvtGC0L3QvtCz0L47Cgo0LiDQp9C10Lwg0LHQvtC70YzRiNC1INC20LjQstC+0YLQvdC+0LUsINGC0LXQvCDQtNC+0LvRjNGI0LUgKirQsdC10YDQtdC80LXQvdC90L7RgdGC0YwqKiDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCoqLCDRgtCw0Log0LrQsNC6INC60YDRg9C/0L3QvtC1INC20LjQstC+0YLQvdC+0LUg0YLRgNC10LHRg9C10YIg0LrQsNC6INCx0L7Qu9GM0YjQvtCz0L4g0YTQvtGA0LzQuNGA0L7QstCw0L3QuNGPINCy0L4g0LLRgNC10LzRjyDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgsINGC0LDQuiDQutCw0Log0Lgg0LHQvtC70YzRiNC10LPQviDQstGA0LXQvNC10L3QuCDQstC30YDQvtGB0LvQtdC90LjRjzsKCjUuINCi0LDQutC20LUg0L7RgiDRgNCw0LfQvNC10YDQsCAqKtC80L7Qt9Cz0LAqKiDQt9Cw0LLQuNGB0LjRgiAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Ywg0LbQuNC30L3QuCoqLCDQvtC00L3QsNC60L4g0Y3RgtCwINC30LDQstC40YHQuNC80L7RgdGC0Ywg0L3QtSDRj9Cy0LvRj9C10YLRgdGPINC/0YDRj9C80L7QuSwg0YLQsNC6INC60LDQuiDQtdGB0LvQuCDQvNGLINGA0LDRgdGB0LzQvtGC0YDQuNC8INC00L7Qu9GOICoq0LLQtdGB0LAg0LzQvtC30LPQsCoqINC6ICoq0LLQtdGB0YMg0YLQtdC70LAqKiwg0YLQviDQutC+0YDRgNC10LvRj9GG0LjRjyDQt9C90LDRh9C40YLQtdC70YzQvdC+INGD0L/QsNC00ZHRgjsKCjYuINCX0LDQstC40YHQuNC80L7RgdGC0Lgg0LzQtdC20LTRgyAqKtGB0L3QvtC8Kiog0LggKirQstC10YHQvtC8INGC0LXQu9CwKiosICoq0YHQvdC+0LwqKiDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0YzRjiDQttC40LfQvdC4Kiog0LjQu9C4ICoq0YHQvdC+0LwqKiDQuCAqKtCy0YDQtdC80LXQvdC4INCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqINC00L7RgdGC0LDRgtC+0YfQvdC+INGB0LvQvtC20L3QviDQvtCx0YrRj9GB0L3QuNGC0YwsINC00LvRjyDRjdGC0L7Qs9C+INC90YPQttC90L4g0YPQs9C70YPQsdC70Y/RgtGB0Y8g0LIg0YHQu9C+0LbQvdGL0LUg0LHQuNC+0LvQvtCz0LjRh9C10YHQuNC60LUg0L/RgNC+0YbQtdGB0YHRiy4KCiMjIDExLiDQmtC+0Y3RhNGE0LjRhtC40LXQvdGCINCh0L/QuNGA0LzQtdC90LAKCtCf0YDQvtCy0LXRgNC40LwsINGH0YLQviDRgNCw0L3Qs9C+0LLRi9C5INC60L7RjdGE0YTQuNGG0LjQtdC90YIg0LrQvtGA0YDQtdC70Y/RhtC40Lgg0KHQv9C40YDQvNC10L3QsCDQv9GA0LjQvNC10YDQvdC+INGA0LDQstC10L0g0J/QuNGA0YHQvtC90YM6CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkZi5sb2cub3V0IHw+IAogZHBseXI6OnNlbGVjdCgtTkFNRSwgLUVYUF9JTkQsIC1QUkVEX0lORCwgLURBTkdfSU5EKSB8PiAKICBwYWlyd2lzZS5jb3IobmFtZV9tZXRob2QgPSAic3BlYXJtYW4iKSB8PiAKICBwcmludF9kZigpCmBgYAoK0KDQtdC30YPQu9GM0YLQsNGC0Ysg0YHRhdC+0LbQuCDQuCDRjdGC0L4g0L3QtSDRg9C00LjQstC40YLQtdC70YzQvdC+LCDQstC10LTRjCDQvNGLINGB0YfQuNGC0LDQtdC8LCDRh9GC0L4g0LTQsNC90L3Ri9C1INC/0L7RhdC+0LbQuCDQvdCwINC90L7RgNC80LDQu9GM0L3Ri9C1LCDQsCDQtNC70Y8g0L3QuNGFINC+0YLQu9C40YfQuNC1INC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQn9C40YDRgdC+0L3QsCDQvtGCINCh0L/QuNGA0LzQtdC90LAg0L3QtdC30L3QsNGH0LjRgtC10LvRjNC90L4uCgojIyAxMi4g0KfQsNGB0YLQvdGL0LUg0LrQvtGA0YDQtdC70Y/RhtC40L7QvdC90YvQtSDQvtGC0L3QvtGI0LXQvdC40Y8KCtCe0LTQvdCw0LrQviDQstC+0LfQvdC40LrQsNC10YIg0L/QvtC00L7Qt9GA0LXQvdC40LUsINGH0YLQviDQvNC90L7Qs9C40LUg0LfQsNCy0LjRgdC40LzQvtGB0YLQuCDQuNC80LXRjtGCINC00YDRg9Cz0YPRjiDQv9GA0LjRh9C40L3Rgywg0L3QtdC20LXQu9C4INC30LDQstC40YHQuNC80L7RgdGC0Ywg0YLQvtC70YzQutC+INC00YDRg9CzINC+0YIg0LTRgNGD0LPQsC4g0JrQsNC6INCx0YvQu9C+INCy0LjQtNC90L4g0L/QviDRj9GJ0LjQutCw0Lwg0YEg0YPRgdCw0LzQuCDQtNC70Y8gKirQuNC90LTQtdC60YHQsCDQvtC/0LDRgdC90L7RgdGC0Lgg0LzQtdGB0YLQsCDQtNC70Y8g0YHQvdCwKiog0LXRgdGC0Ywg0LzQvtC90L7RgtC+0L3QvdCw0Y8g0LfQsNCy0LjRgdC40LzQvtGB0YLRjCDQv9C+0YfRgtC4INCy0YHQtdGFINC/0YDQuNC30L3QsNC60L7QsiDQvtGCINGN0YLQvtCz0L4g0LjQvdC00LXQutGB0LAuINCf0L7RjdGC0L7QvNGDINCy0L7Qt9C90LjQutCw0LXRgiDQuNC00LXRjyDQv9GA0L7QstC10YDQutC4INC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDRh9Cw0YHRgtC90L7QuSDQutC+0YDRgNC10LvRj9GG0LjQuCDRgSDQstGL0YfQtdGC0L7QvCDQstC70LjRj9C90LjRjyDRjdGC0L7Qs9C+INC40L3QtNC10LrRgdCwLgoKYGBge3J9CmRmLmxvZy5vdXQgfD4gCiAgZ3JvdXBfYnkoRVhQX0lORCkgfD4gCiAgbXV0YXRlKEJPRFlfV0VJID0gQk9EWV9XRUkgLSBtZWFuKEJPRFlfV0VJLCBuYS5ybSA9IFRSVUUpLCBCUkFJTl9XRSA9IEJSQUlOX1dFIC0gbWVhbihCUkFJTl9XRSwgbmEucm0gPSBUUlVFKSwgU0xPV1dBVkUgPSBTTE9XV0FWRSAtIG1lYW4oU0xPV1dBVkUsIG5hLnJtID0gVFJVRSksIFBBUkFET1ggPSBQQVJBRE9YIC0gbWVhbihQQVJBRE9YLCBuYS5ybSA9IFRSVUUpLCBTTEVFUCA9IFNMRUVQIC0gbWVhbihTTEVFUCwgbmEucm0gPSBUUlVFKSwgTElGRVNQQU4gPSBMSUZFU1BBTiAtIG1lYW4oTElGRVNQQU4sIG5hLnJtID0gVFJVRSksIEdFU1RUSU1FID0gR0VTVFRJTUUgLSBtZWFuKEdFU1RUSU1FLCBuYS5ybSA9IFRSVUUpKSB8PgogIGdyb3VwX2J5KC5kcm9wID0gVFJVRSkgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSwgLUVYUF9JTkQsIC1QUkVEX0lORCwgLURBTkdfSU5EKSB8PiAKICBwYWlyd2lzZS5jb3IoKSB8PiAKICBwcmludF9kZigpCmBgYAoK0JfQtNC10YHRjCDQvNGLINCy0LjQtNC40LwsINGH0YLQviDQtNC+0YHRgtCw0YLQvtGH0L3QviDQvNC90L7Qs9C+INC30LDQstC40YHQuNC80L7RgdGC0LXQuSDQv9C10YDQtdGB0YLQsNC70Lgg0LHRi9GC0Ywg0LfQvdCw0YfQuNC80YvQvNC4ICgkXGFscGhhID0gMC4wNSQpLCDRgtCw0Log0LrQsNC6INC80Ysg0LjRgdC60LvRjtGH0LjQu9C4INCy0LvQuNGP0L3QuNC1ICoq0LjQvdC00LXQutGB0LAg0L7Qv9Cw0YHQvdC+0YHRgtC4INC80LXRgdGC0LAg0LTQu9GPINGB0L3QsCoqLiDQntC20LjQtNCw0LXQvNC+INC30LDQstC40YHQuNC80L7RgdGC0Lgg0LLRgdC10YUg0LLQuNC00L7QsiAqKtGB0L3QsCoqINC+0YIgKirQstC10YHQsCDRgtC10LvQsCoqLCAqKtCy0LXRgdCwINC80L7Qt9Cz0LAqKiDQuCAqKtC/0YDQvtC00L7Qu9C20LjRgtC10LvRjNC90L7RgdGC0Lgg0YHQvdCwKiog0LfQvdCw0YfQuNGC0LXQu9GM0L3QviDQvtGB0LvQsNCx0LvQuCwg0L7QtNC90LDQutC+INC30LDQstC40YHQuNC80L7RgdGC0Lgg0YHQvdCwINC+0YIgKirQstGA0LXQvNC10L3QuCDQsdC10YDQtdC80LXQvdC90L7RgdGC0LgqKiDRg9C80LXQvdGM0YjQuNC70LjRgdGMINC80LDQu9C+LgoKIyMgMTMuINCc0L3QvtC20LXRgdGC0LLQtdC90L3QsNGPINGA0LXQs9GA0LXRgdGB0LjRjwoK0J/QvtGB0LzQvtGC0YDQuNC8INC90LAg0LTQsNC90L3Ri9C1OgoKYGBge3Igd2FybmluZz1GQUxTRX0KZGYubG9nLm91dCB8PiBkcGx5cjo6c2VsZWN0KC1OQU1FKSB8PiBnZ3BhaXJzKGNvbHVtbnMgPSAxOjcpCmBgYAoK0JLRgdGRLCDRh9GC0L4g0L3Rg9C20L3Qviwg0LHRi9C70L4g0L/RgNC+0LvQvtCz0LDRgNC40YTQvNC40YDQvtCy0LDQvdC+INGA0LDQvdGM0YjQtSwg0L/QvtGN0YLQvtC80YMg0L/RgNC40YHRgtGD0L/QuNC8INC6INGA0LXQs9GA0LXRgdGB0LjQuC4g0KXQvtGC0LjQvCDQv9GA0L7Qs9C90L7Qt9C40YDQvtCy0LDRgtGMINCy0YDQtdC80Y8g0LbQuNC30L3QuCDQv9C+INC+0YHRgtCw0LvRjNC90YvQvCDQtNCw0L3QvdGL0LwuCgrQodGC0YDQvtC40Lwg0YDQtdCz0YDQtdGB0YHQuNGOINC/0L4g0LLRgdC10Lwg0L/RgNC40LfQvdCw0LrQsNC8OgoKYGBge3J9CmRmLmxvZy5vdXQubmEgPC0gZGYubG9nLm91dCB8PiBkcGx5cjo6ZmlsdGVyKCFpcy5uYShTTE9XV0FWRSkgJiAhaXMubmEoUEFSQURPWCkgJiAhaXMubmEoU0xFRVApICYgIWlzLm5hKExJRkVTUEFOKSAmICFpcy5uYShHRVNUVElNRSkpCm1vZGVsIDwtIGxtKExJRkVTUEFOIH4gLiwgZGYubG9nLm91dC5uYSB8PiBkcGx5cjo6c2VsZWN0KC1OQU1FKSkKbW9kZWwuYmV0YSA8LSBsbS5iZXRhKG1vZGVsKQpzdW1tYXJ5KG1vZGVsLmJldGEpCmBgYAoK0KDQtdCz0YDQtdGB0YHQuNGPINC30L3QsNGH0LjQvNCwLCDQvdC+INC30L3QsNGH0LjQvNGL0YUg0LrQvtGN0YTRhNC40YbQuNC10L3RgtC+0LIg0L3QsCDRg9GA0L7QstC90LUgJFxhbHBoYSA9IDAuMDUkINGC0L7Qu9GM0LrQviDQtNCy0LA6INC00LvRjyAqKtCy0LXRgdCwINC80L7Qt9Cz0LAqKiDQuCDQtNC70Y8g0YTQuNC60YLQuNCy0L3QvtC5INC/0LXRgNC10LzQtdC90L3QvtC5ICoq0LjQvdC00LXQutGB0LAg0YXQuNGJ0L3QuNGH0LXRgdGC0LLQsCoqINC/0YDQuCDQt9C90LDRh9C10L3QuNC4ICQzJC4KCiMjIDE0LiDQlNC+0LLQtdGA0LjRgtC10LvRjNC90YvQtSDRjdC70LvQuNC/0YHRiwoK0J/QvtGB0YfQuNGC0LDQtdC8INC60L7QstCw0YDQuNCw0YbQuNC+0L3QvdGD0Y4g0Lgg0LrQvtGA0YDQtdC70Y/RhtC40L7QvdC90YPRjiDQvNCw0YLRgNC40YbRgyDQtNC70Y8g0LrQvtGN0YTRhNC40YbQuNC10L3RgtC+0LIg0YDQtdCz0YDQtdGB0YHQuNC4OgoKYGBge3J9CmR1bW15LnZhcmlhYmxlIDwtIGRhdGEuZnJhbWUoTkFNRSA9IGRmLmxvZy5vdXQubmEkTkFNRSkKCmZvciAoaSBpbiAyOjUpewogIGR1bW15LnZhcmlhYmxlW1twYXN0ZSgiUFJFRF9JTkQiLCBpLCBzZXAgPSAiXyIpXV0gPSBpZmVsc2UoZGYubG9nLm91dC5uYSRQUkVEX0lORCA9PSBpLCAxLCAwKQp9CmZvciAoaSBpbiAyOjUpewogIGR1bW15LnZhcmlhYmxlW1twYXN0ZSgiRVhQX0lORCIsIGksIHNlcCA9ICJfIildXSA9IGlmZWxzZShkZi5sb2cub3V0Lm5hJEVYUF9JTkQgPT0gaSwgMSwgMCkKfQpmb3IgKGkgaW4gMjo1KXsKICBkdW1teS52YXJpYWJsZVtbcGFzdGUoIkRBTkdfSU5EIiwgaSwgc2VwID0gIl8iKV1dID0gaWZlbHNlKGRmLmxvZy5vdXQubmEkREFOR19JTkQgPT0gaSwgMSwgMCkKfQoKbi5kZiA8LSBsZW5ndGgoZGYubG9nLm91dC5uYSROQU1FKQpzaWdtYS5kZiA8LSBzdW0obW9kZWwkcmVzaWR1YWxzICoqIDIpIC8gKG4uZGYgLSBtb2RlbCRyYW5rKQpjb3ZNYXRyaXguZGYgPC0gZGYubG9nLm91dC5uYSB8PiBkcGx5cjo6c2VsZWN0KC1OQU1FLCAtUFJFRF9JTkQsIC1FWFBfSU5ELCAtREFOR19JTkQpIHw+IGNiaW5kKGR1bW15LnZhcmlhYmxlIHw+IGRwbHlyOjpzZWxlY3QoLU5BTUUpKSB8PiBjb3YoKQpjb3ZfYi5kZiA8LSBzaWdtYS5kZiAvIG4uZGYgKiBzb2x2ZShjb3ZNYXRyaXguZGYpCmNvdl9iZXRhLmRmIDwtIHNpZ21hLmRmIC8gbi5kZiAqIHNvbHZlKGNvdjJjb3IoY292TWF0cml4LmRmKSkKY29yX2IuZGYgPC0gY292MmNvcihjb3ZfYi5kZikKY29ycnBsb3QoY29yX2IuZGYsIG1ldGhvZCA9ICJjb2xvciIpCmBgYAoK0JLQuNC00LjQvCDRgdC40LvRjNC90YPRjiDQutC+0YDRgNC10LvRj9GG0LjRjiDQvNC10LbQtNGDINC80LXQttC00YMg0L7RhtC10L3QutCw0LzQuCDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0L7QsiDRgNC10LPRgNC10YHRgdC40Lgg0LzQtdC20LTRgyAqKtCy0LXRgdC+0Lwg0YLQtdC70LAqKiDQuCAqKtC80L7Qt9Cz0LAqKiwg0LzQtdC20LTRgyDQstGB0LXQvNC4INCy0LjQtNCw0LzQuCAqKtGB0L3QsCoqLCDQvNC10LbQtNGDICoq0LjQvdC00LXQutGB0LDQvNC4INC+0LHRidC10Lkg0L7Qv9Cw0YHQvdC+0YHRgtC4Kiog0LggKirRhdC40YnQvdC40YfQtdGB0YLQstCwKiosINCwINGC0LDQutC20LUg0LzQtdC20LTRgyAqKtCy0LXRgdC+0Lwg0LzQvtC30LPQsCoqINC4ICoq0L/RgNC+0LTQvtC70LbQuNGC0LXQu9GM0L3QvtGB0YLRjNGOINC20LjQt9C90LgqKi4KCtCf0L7RgdC80L7RgtGA0LjQvCDQvdCwINC00L7QstC10YDQuNGC0LXQu9GM0L3Ri9C5INGN0LvQu9C40L/RgdC+0LjQtCDQtNC70Y8g0LfQvdCw0YfQuNC80YvRhSDQutC+0Y3RhNGE0LjRhtC40LXQvdGC0L7Qsiwg0L7QsdC+0LfQvdCw0YfQtdC90L3Ri9GFINCy0YvRiNC1OgoKYGBge3J9CnBsb3QoZWxsaXBzZTo6ZWxsaXBzZShjb3ZfYmV0YS5kZltjKDIsIDkpLCBjKDIsIDkpXSwgY2VudHJlID0gbW9kZWwuYmV0YSRzdGFuZGFyZGl6ZWQuY29lZmZpY2llbnRzW2MoMywgOSldLCBsZXZlbD0wLjk1LCBucG9pbnRzID0gMTAwKSwgdHlwZSA9ICJsIiwgYXNwID0gMSkKbGluZXMoeCA9IGMoLTIsIDIpLCB5ID0gYygyLCAtMiksIGNvbCA9ICJyZWQiKQpgYGAKCtCe0L0g0LjQvNC10LXRgiDQv9C70L7RhdC+0Lkg0LLQuNC0OiDQu9C40LHQviDQvtCx0LAg0LjQvNC10LXRgiDRgdC40LvRjNC90L7QtSDQstC70LjRj9C90LjQtSwg0LvQuNCx0L4g0L7QsdCwIC0tLSDRgdC70LDQsdC+0LUuINCf0YDQuNGH0ZHQvCAqKtC40L3QtNC10LrRgSDRhdC40YnQvdC40YfQtdGB0YLQstCwKiog0LzQvtC20LXRgiDQsdGL0YLRjCDRgdC+0LLRgdC10Lwg0L3QtSDQt9C90LDRh9C40LwsINGC0LDQuiDQutCw0Log0Y3Qu9C70LjQv9GBINC/0LXRgNC10YHQtdC60LDQtdGCINC90YPQu9C10LLQvtC1INC30L3QsNGH0LXQvdC40LUg0L/QviAkeSQuCgojIyAxNS4g0KHQuNC70YzQvdC+INC60L7RgNGA0LXQu9C40YDRg9GO0YnQuNC1INC/0YDQuNC30L3QsNC60LgKCtCf0L7RgdGC0YDQvtC40Lwg0YLQsNCx0LvQuNGG0YMgVklGOgoKYGBge3J9Cm9sc192aWZfdG9sKG1vZGVsKQpgYGAKCtCd0LDQsdC70Y7QtNCw0LXQvCDQvtGH0LXQvdGMINCx0L7Qu9GM0YjRg9GOINC80YPQu9GM0YLQuNC60L7Qu9C70LjQvdC10LDRgNC90L7RgdGC0Ywg0L/QvtGH0YLQuCDQv9C+INCy0YHQtdC8INC/0YDQuNC30L3QsNC60LDQvC4KCtCi0LXQv9C10YDRjCDRgtCw0LHQu9C40YbQsCDRgSDRh9Cw0YHRgtC40YfQvdGL0LzQuCDQutC+0YDRgNC10LvRj9GG0LjRj9C80Lg6CgpgYGB7cn0Kb2xzX2NvcnJlbGF0aW9ucyhtb2RlbCkKYGBgCgrQodC40LvRjNC90L7QtSDQstC70LjRj9C90LjQtSDQvdCwINC30LDQstC40YHQuNC80YPRjiDQv9C10YDQtdC80LXQvdC90YPRjiDQt9CwINC40YHQutC70Y7Rh9C10L3QuNC10Lwg0L7RgdGC0LDQu9GM0L3Ri9GFINC+0LrQsNC30YvQstCw0Y7Rgiwg0LIg0L/QtdGA0YPRjiDQvtGH0LXRgNC10LTRjCwgKirQstC10YEg0LzQvtC30LPQsCoqLCDQsCDRgtCw0LrQttC1ICoq0LjQvdC00LXQutGBINGF0LjRidC90LjRh9C10YHRgtCy0LAqKiDQuCAqKtC+0LHRidC40Lkg0LjQvdC00LXQutGBINC+0L/QsNGB0L3QvtGB0YLQuCoqICjRhdC+0YLRjyDQvtC9INGB0LjQu9GM0L3QviDQt9Cw0LLQuNGB0LjRgiDQvtGCICoq0LjQvdC00LXQutGB0LAg0YXQuNGJ0L3QuNGH0LXRgdGC0LLQsCoqKS4KCtCf0L7Qv9GA0L7QsdGD0LXQvCDRg9Cx0YDQsNGC0Ywg0YHQuNC70YzQvdC+INC60L7RgNGA0LXQu9C40YDRg9GO0YnQuNC1LiDQo9Cx0LXRgNGR0LwgKirQstC10YEg0YLQtdC70LAqKiwgKirQvtCx0YnQtdC1INCy0YDQtdC80Y8g0YHQvdCwKiog0LggKirQvtCx0YnQuNC5INC40L3QtNC10LrRgSDQvtC/0LDRgdC90L7RgdGC0LgqKjoKCmBgYHtyfQptb2RlbC5taW51c2NvciA8LSBsbShMSUZFU1BBTiB+IC4sIGRmLmxvZy5vdXQubmEgfD4gZHBseXI6OnNlbGVjdCgtTkFNRSwgLUJPRFlfV0VJLCAtIFNMRUVQLCAtREFOR19JTkQpKQptb2RlbC5taW51c2Nvci5iZXRhIDwtIGxtLmJldGEobW9kZWwubWludXNjb3IpCnN1bW1hcnkobW9kZWwubWludXNjb3IuYmV0YSkKYGBgCtCf0L4g0LfQvdCw0YfQuNC80L7RgdGC0Lgg0LjQvNC10LXQvCDRgtGDINC20LUg0YHQuNGC0YPQsNGG0LjRjiwg0L7QtNC90LDQutC+INC30L3QsNGH0LjQvNC+0YHRgtGMINC/0L4gcC12YWx1ZSDQv9C+0LTRgNC+0YHQu9CwLCDQsCAkUl4yJCDQv9C+0YfRgtC4INC90LUg0LjQt9C80LXQvdC40LvRgdGPLgoK0JzRg9C70YzRgtC40LrQvtC70LvQuNC90LXQsNGA0L3QvtGB0YLRjCDQuNGB0YfQtdC30LvQsDoKCmBgYHtyfQpvbHNfdmlmX3RvbChtb2RlbC5taW51c2NvcikKYGBgCgojIyAxNi4g0J/QvtGI0LDQs9C+0LLQsNGPINGA0LXQs9GA0LXRgdGB0LjRjwoKIyMjIEJhY2t3YXJkCgrQn9C+0YHRgtGA0L7QuNC8INC/0L7RiNCw0LPQvtCy0YPRjiDRgNC10LPRgNC10YHRgdC40Y4uINCh0L3QsNGH0LDQu9CwINGA0LDRgdGB0LzQvtGC0YDQuNC8INGD0LHQsNCy0LvQtdC90LjQtSDQutC+0LvQuNGH0LXRgdGC0LLQsCDQv9Cw0YDQsNC80LXRgtGA0L7QsjoKCmBgYHtyfQpzdC5idy5sbSA8LSBvbHNfc3RlcF9iYWNrd2FyZF9wKG1vZGVsLm1pbnVzY29yKQpzdC5idy5sbQpgYGAKCtCa0LDQuiDRjdGC0L4g0LLRi9Cz0LvRj9C00LjRgiDQv9C+INGI0LDQs9Cw0Lw6CgpgYGB7cn0KcGxvdChzdC5idy5sbSkKYGBgCgrQmtCw0LrQsNGPINC20LUg0LzQvtC00LXQu9GMINC/0L7Qu9GD0YfQsNC10YLRgdGPINCyINC40YLQvtCz0LU6CgpgYGB7cn0Kc3VtbWFyeShzdC5idy5sbSRtb2RlbCkKYGBgCgrQntGB0YLQsNC70L7RgdGMICQyJCDQv9GA0LjQt9C90LDQutCwLCDRg9C00LDQu9C40LvQuNGB0YwgKirQstGA0LXQvNGPINCx0LXRgNC10LzQtdC90L3QvtGB0YLQuCoqLCAqKtCx0YvRgdGC0YDRi9C5INGB0L7QvSoqLCAqKtC80LXQtNC70LXQvdC90YvQuSDRgdC+0L0qKiDQuCAqKtC40L3QtNC10LrRgSDQstC+0LfQtNC10LnRgdGC0LLQuNGPINGB0L3QsCoqLiAKCiMjIyBGb3J3YXJkCgrQotC10L/QtdGA0Ywg0LHRg9C00LXQvCDQtNC+0LHQsNCy0LvRj9GC0Ywg0L/RgNC40LfQvdCw0LrQuDoKCmBgYHtyfQpzdC5mdy5sbSA8LSBvbHNfc3RlcF9mb3J3YXJkX3AobW9kZWwubWludXNjb3IpCnN0LmZ3LmxtCmBgYAoK0JrQsNC6INGN0YLQviDQstGL0LPQu9GP0LTQtdC70L4g0L3QsCDQs9GA0LDRhNC40LrQtToKCmBgYHtyfQpwbG90KHN0LmZ3LmxtKQpgYGAKCtCa0LDQutGD0Y4g0LbQtSDQvNC+0LTQtdC70Ywg0L/QvtC70YPRh9C40LvQuDoKCmBgYHtyfQpzdW1tYXJ5KHN0LmZ3LmxtJG1vZGVsKQpgYGAKCtCe0L3QsCDQsNC90LDQu9C+0LPQuNGH0L3QsCDRgtC+0LksINC60L7RgtC+0YDRg9GOINC/0L7Qu9GD0YfQuNC70Lgg0L7RgiBiYWNrd2FyZC4KCtCe0LTQvdCw0LrQviDRgtC+0LvRjNC60L4g0L/RgNC4INC+0LTQvdC+0Lwg0LfQvdCw0YfQtdC90LjQuCAqKtC40L3QtNC10LrRgdCwINGF0LjRidC90LjRh9C10YHRgtCy0LAqKiDQutC+0Y3RhNGE0LjRhtC40LXQvdGCINC30L3QsNGH0LjQvNGL0LksINC/0L7RjdGC0L7QvNGDINCy0YDRg9GH0L3Rg9GOINGB0L7Qt9C00LDQtNC40Lwg0YTQuNC60YLQuNCy0L3Ri9C1INC/0LXRgNC10LzQtdC90L3Ri9C1INC4INC/0YDQvtCy0LXQtNGR0Lwg0L7Qv9GP0YLRjCDQsNCy0YLQvtC80LDRgtC40YfQtdGB0LrQuNC5INC+0YLQsdC+0YA6CgpgYGB7cn0KZGYubG9nLm91dC5kdW1teSA8LSBkZi5sb2cub3V0Lm5hCmZvciAoaSBpbiAyOjUpewogIGRmLmxvZy5vdXQuZHVtbXlbW3Bhc3RlKCJQUkVEX0lORCIsIGksIHNlcCA9ICJfIildXSA8LSBpZmVsc2UoZGYubG9nLm91dC5kdW1teSRQUkVEX0lORCA9PSBpLCAxLCAwKQp9Cm1vZGVsLm91dC5kdW1teSA8LSBsbShMSUZFU1BBTiB+IEJSQUlOX1dFICsgUFJFRF9JTkRfMiArIFBSRURfSU5EXzMgKyBQUkVEX0lORF80ICsgUFJFRF9JTkRfNSwgZGF0YSA9IGRmLmxvZy5vdXQuZHVtbXkpCnN0LmJ3LmxtLmR1bW15IDwtIG9sc19zdGVwX2JhY2t3YXJkX3AobW9kZWwub3V0LmR1bW15KQpwbG90KHN0LmJ3LmxtLmR1bW15KQpzdW1tYXJ5KHN0LmJ3LmxtLmR1bW15JG1vZGVsKQpgYGAKCtCe0YHRgtCw0LvQvtGB0Ywg0LTQstCwINC30L3QsNGH0LXQvdC40Y8gKirQuNC90LTQtdC60YHQsCDRhdC40YnQvdC40YfQtdGB0YLQstCwKiouCgojIyAxNy4g0J7RgdGC0LDRgtC60Lgg0LggUHJlZGljdGVkIHZzIFJlc2lkdWFscwoK0J3QsNGI0LvQuCDQu9GD0YfRiNGD0Y4g0LzQvtC00LXQu9GMINGA0LXQs9GA0LXRgdGB0LjQuC4g0JTQvtCx0LDQstC40Lwg0YLQtSDQvdCw0LHQu9GO0LTQtdC90LjRjywg0LrQvtGC0L7RgNGL0LUg0LjQvNC10LvQuCDQv9GA0L7Qv9GD0YnQtdC90L3Ri9C1INC30L3QsNGH0LXQvdC40Y8g0YLQvtC70YzQutC+INC90LAg0YPQsdGA0LDQvdC90YvRhSDQtNCw0L3QvdGL0YU6CgpgYGB7cn0KZGYubG9nLm5hLmJlc3QgPC0gZGYubG9nIHw+IGZpbHRlcighaXMubmEoTElGRVNQQU4pKQpmb3IgKGkgaW4gMjo1KXsKICBkZi5sb2cubmEuYmVzdFtbcGFzdGUoIlBSRURfSU5EIiwgaSwgc2VwID0gIl8iKV1dIDwtIGlmZWxzZShkZi5sb2cubmEuYmVzdCRQUkVEX0lORCA9PSBpLCAxLCAwKQp9Cm1vZGVsLmJlc3QgPC0gbG0oTElGRVNQQU4gfiBCUkFJTl9XRSArIFBSRURfSU5EXzIgKyBQUkVEX0lORF8zLCBkYXRhID0gZGYubG9nLm5hLmJlc3QpCm1vZGVsLmJldGEuYmVzdCA8LSBsbS5iZXRhKG1vZGVsLmJlc3QpCnN1bW1hcnkobW9kZWwuYmV0YS5iZXN0KQpgYGAKCtCv0YHQvdC+LCDRh9GC0L4g0YHQvtCz0LvQsNGB0L7QstCw0L3QvdC+0YHRgtGMINC80L7QtNC10LvQuCDRg9C/0LDQu9CwLCDRgtCw0Log0LrQsNC6INC80Ysg0L/QvtGB0YLRgNC+0LjQu9C4INC10ZEg0L3QsCDQsdC+0LvRjNGI0LXQvCDQvdCw0LHQvtGA0LUg0LTQsNC90L3Ri9GFICjQv9C70Y7RgSDQsdGL0LvQuCDQtNC+0LHQsNCy0LvQtdC90Ysg0LLRi9Cx0YDQvtGB0YssINGD0LTQsNC70LXQvdC90YvQtSDRgNCw0L3RjNGI0LUpLgoK0KLQtdC/0LXRgNGMINC/0L7RgdC80L7RgtGA0LjQvCDQvdCwINC90L7RgNC80LDQu9GM0L3QvtGB0YLRjCDQvtGB0YLQsNGC0LrQvtCyICjRhdC+0YLQuNC8INGC0L7Rh9C90L7RgdGC0Ywg0L/RgNC+0LLQtdGA0Y/QtdC80YvRhSDQutGA0LjRgtC10YDQuNC10LIpOgoKYGBge3J9CnBsb3QobW9kZWwuYmVzdCwgd2hpY2g9MikKYGBgCgrQktC40LTQuNC8LCDRh9GC0L4g0YHQv9GA0LDQstCwINC90L7RgNC80LDQu9GM0L3QvtGB0YLQuCDQvdC1INC90LDQsdC70Y7QtNCw0LXRgtGB0Y8sINGF0L7RgtGPINC90LAg0L7RgdC90L7QstC90L7QvCDQvNCw0YHRgdC40LLQtSDQtNCw0L3QvdGL0YUg0L3QvtGA0LzQsNC70YzQvdC+0YHRgtGMINC/0YDQuNGB0YPRgtGB0YLQstGD0LXRgi4KCtCi0LXQv9C10YDRjCDQv9C+0YHQvNC+0YLRgNC40Lwg0L3QsCDQs9GA0LDRhNC40LogUHJlZGljdGVkIHZzIFJlc2lkdWFsczoKCmBgYHtyfQpwbG90KG1vZGVsLmJlc3Qsd2hpY2g9MSkKYGBgCgrQktC40LTQuNC8INC00L7RgdGC0LDRgtC+0YfQvdC+INGA0LDQstC90L7QvNC10YDQvdC+0LUg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40LUg0LIg0L/RgNGP0LzQvtGD0LPQvtC70YzQvdC+0Lkg0L7QsdC70LDRgdGC0Lgg0L7QutC+0LvQviDQvdGD0LvRjy4g0KLQviDQtdGB0YLRjCDQvNC+0LbQvdC+INC/0YDQtdC00L/QvtC70L7QttC40YLRjCDQsNC00LXQutCy0LDRgtC90L7RgdGC0Ywg0LLRi9Cx0YDQsNC90L3QvtC5INC70LjQvdC10LnQvdC+0Lkg0LzQvtC00LXQu9C4LCDQsCDRgtCw0LrQttC1INCz0L7QvNC+0LPQtdC00L7RgdGC0LjRh9C90L7RgdGC0YwgKNGC0L4g0LXRgdGC0Ywg0YDQsNCy0LXQvdGB0YLQstC+INC00LjRgdC/0LXRgNGB0LjQuSDQvtGB0YLQsNGC0LrQvtCyKS4KCtCT0YDQsNGE0LjQuiBSZXNpZHVscyB2cyBEZWxldGVkIFJlc2lkdWFsczoKCmBgYHtyfQpkZWxfcmVzIDwtIHNhcHBseSgxOjU4LCBmdW5jdGlvbihuKSBkZi5sb2cubmEuYmVzdCRMSUZFU1BBTltuXSAtIHByZWRpY3QobG0oTElGRVNQQU4gfiBCUkFJTl9XRSArIFBSRURfSU5EXzIgKyBQUkVEX0lORF8zLCBkYXRhID0gZGYubG9nLm5hLmJlc3RbLW4sIF0pLCBkZi5sb2cubmEuYmVzdFtuLCBdKSkKcGxvdCh4ID0gbW9kZWwuYmVzdCRyZXNpZHVhbHMsIHkgPSBkZWxfcmVzLCB4bGFiID0gIlJzaWR1YWxzIiwgeWxhYiA9ICJEZWxldGVkIHJlc2lkdWFscyIpCmxpbmVzKHggPSBjKC0zLCAzKSwgeSA9IGMoLTMsIDMpKQphYmxpbmUobG0oZGVsX3JlcyB+IG1vZGVsLmJlc3QkcmVzaWR1YWxzKSwgY29sID0gImJsdWUiKQp0ZXh0KHggPSBtb2RlbC5iZXN0JHJlc2lkdWFscywgeSA9IGRlbF9yZXMsIGxhYmVscyA9IHJvd25hbWVzKGRmLmxvZy5uYS5iZXN0KSwgY2V4ID0gMC42LCBwb3MgPSA0LCBjb2wgPSAicmVkIikKYGBgCgrQmtCw0Log0Lgg0L7QttC40LTQsNC70L7RgdGMLCDRgtC+0YfQutC4INGA0LDRgdC/0L7Qu9C+0LbQuNC70LjRgdGMINC/0L7QtCDRh9GD0YLRjCDQsdC+0LvRjNGI0LjQvCDQvdCw0LrQu9C+0L3QvtC8LCDQvdC10LbQtdC70LggJHkgPSB4JC4g0JHQvtC70YzRiNC40YUg0L7RgtC60LvQvtC90LXQvdC40LkgKNC/0L4g0LvQuNC90LjQuCDRgNC10LPRgNC10YHQuNC4INC90LAg0Y3RgtC40YUg0YLQvtGH0LrQsNGFKSDQvdC1INCy0LjQtNC90L4sINGC0L4g0LXRgdGC0Ywg0YHQutCw0LfQsNGC0Ywg0YfRgtC+LdGC0L4g0L4g0LLRi9Cx0YDQvtGB0LDRhSDQvdC10LvRjNC30Y8uCgojIyAxOC4g0JLRi9Cx0YDQvtGB0Ysg0L/QviDQnNCw0YXQsNC70LDQvdC+0LHQuNGB0YMKCmBgYHtyfQpkZi5sb2cubmEuYmVzdC54IDwtIGRmLmxvZy5uYS5iZXN0IHw+IGRwbHlyOjpzZWxlY3QoQlJBSU5fV0UsIFBSRURfSU5EXzIsIFBSRURfSU5EXzMpCgpjb3ZfbWF0cml4IDwtIGNvdihkZi5sb2cubmEuYmVzdC54KQoKbWFoYWxhbm9iaXNfZGlzdGFuY2UgPC0gbWFoYWxhbm9iaXMoZGYubG9nLm5hLmJlc3QueCwgY2VudGVyID0gY29sTWVhbnMoZGYubG9nLm5hLmJlc3QueCksIGNvdiA9IGNvdl9tYXRyaXgpCgpwbG90KG1haGFsYW5vYmlzX2Rpc3RhbmNlLCAKICAgICBtYWluID0gIk1haGFsYW5vYmlzIERpc3RhbmNlIFBsb3QiLAogICAgIHhsYWIgPSAiT2JzZXJ2YXRpb24iLAogICAgIHlsYWIgPSAiTWFoYWxhbm9iaXMgRGlzdGFuY2UiLAogICAgIHR5cGUgPSAiaCIsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIpIApwbG90KHNvcnQobWFoYWxhbm9iaXNfZGlzdGFuY2UpLCAKICAgICBtYWluID0gIk1haGFsYW5vYmlzIERpc3RhbmNlIFBsb3QiLAogICAgIHhsYWIgPSAiT2JzZXJ2YXRpb24iLAogICAgIHlsYWIgPSAiTWFoYWxhbm9iaXMgRGlzdGFuY2UiLAogICAgIHR5cGUgPSAiaCIsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIpIApgYGAKCtCf0L4g0JzQsNGF0LDQu9Cw0L3QvtCx0LjRgdGDINCy0LjQtNC10L0g0YHQutCw0YfQvtC6INC00LvRjyDQtNCy0YPRhSDQvdCw0LHQu9GO0LTQtdC90LjQuTogJDEkINC4ICQ0JC4g0K3RgtC+INC00LLQsCDRgdC70L7QvdCwOgoKYGBge3J9CmRmLmxvZy5uYS5iZXN0W2MoMSwgNCksIF0gfD4gcHJpbnRfZGYoKQpgYGAKCiMjIDE5LiDQktGL0LHRgNC+0YHRiyDQv9C+INCa0YPQutGDCgpgYGB7cn0KY29va3NfZGlzdGFuY2UgPC0gY29va3MuZGlzdGFuY2UobW9kZWwuYmVzdCkKCnBsb3QoY29va3NfZGlzdGFuY2UsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIsCiAgICAgdHlwZSA9ICJoIiwgCiAgICAgbWFpbiA9ICJDb29rJ3MgRGlzdGFuY2UiLAogICAgIHhsYWIgPSAiT2JzZXJ2YXRpb24iLAogICAgIHlsYWIgPSAiQ29vaydzIERpc3RhbmNlIikKcGxvdChzb3J0KGNvb2tzX2Rpc3RhbmNlKSwgCiAgICAgY29sID0gImRhcmtibHVlIiwKICAgICB0eXBlID0gImgiLCAKICAgICBtYWluID0gIkNvb2sncyBEaXN0YW5jZSIsCiAgICAgeGxhYiA9ICJPYnNlcnZhdGlvbiIsCiAgICAgeWxhYiA9ICJDb29rJ3MgRGlzdGFuY2UiKQpgYGAKCtCf0L4g0YHQutCw0YfQutGDINC30L3QsNGH0LXQvdC40Y8g0YDQsNGB0YHRgtC+0Y/QvdC40Y8g0JrRg9C60LAg0LzQvtC20L3QviDQstGL0LTQtdC70LjRgtGMINGC0YDQuCDQvdCw0LHQu9GO0LTQtdC90LjRjzogNiwgMTQg0LggMzEuINCt0YLQviDRgtC1INGB0LDQvNGL0LUg0LvQtdGC0YPRh9C40LUg0LzRi9GI0Lgg0Lgg0LXRidGRINC10YXQuNC00L3QsDoKCmBgYHtyfQpkZi5sb2cubmEuYmVzdFtjKDYsIDE0LCAzMSksIF0gfD4gcHJpbnRfZGYoKQpgYGAKCiMjIDIwLiBTdHVkZW50aXplZCBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UgUGxvdAoK0J3QsNC60L7QvdC10YYg0L/QvtGB0LzQvtGC0YDQuNC8INC90LAg0YHQvtC+0YLQvdC+0YjQtdC90LjQtSDRgNGL0YfQsNCz0LAg0Lgg0L7RgdGC0LDRgtC60L7QsjoKCmBgYHtyfQpvbHNfcGxvdF9yZXNpZF9sZXYobW9kZWwuYmVzdCkKYGBgCgrQl9C00LXRgdGMINGP0LLQvdGL0YUg0LLRi9Cx0YDQvtGB0L7QsiDQvdC10YI6INC/0L4g0L7RgdGC0LDRgtC60LDQvCwg0LfQsCDQv9GA0LXQtNC10LvQsNC80Lgg0LTQvtCy0LXRgNC40YLQtdC70YzQvdC+0LPQviDQuNC90YLQtdGA0LLQsNC70LAg0L3QtdCx0L7Qu9GM0YjQvtC1INC60L7Qu9C40YfQtdGB0YLQstC+INC90LDQsdC70Y7QtNC10L3QuNC5LCDQv9C+INGA0YvRh9Cw0LPRgyDQtNCy0LAg0LrQsNC90LTQuNC00LDRgtCwINC90LAg0LLRi9Cx0YDQvtGB0YsgLS0tINGB0LvQvtC90YssINCwINC90LDQsdC70Y7QtNC10L3QuNC5LCDQutC+0YLQvtGA0YvQtSDQsdGLINCx0YvQu9C4INC4INC/0L4g0YDRi9GH0LDQs9GDINC4INC/0L4g0L7RgdGC0LDRgtC60LDQvCAi0LLRi9Cx0YDQvtGB0LDQvNC4Iiwg0L3QtdGCLgoKIyMgMjEuINCY0YHQutC70Y7Rh9Cw0LXQvCDQstGL0LHRgNC+0YHRiwoK0J3QsNC50LTQtdC90L3Ri9C1INCy0YvQsdGA0L7RgdGLINC40LzQtdGO0YIg0L7Qv9GA0LXQtNC10LvRkdC90L3Ri9C5INGB0LzRi9GB0LsgLS0tINCx0L7Qu9GM0YjQuNC1INC4INGB0YDQtdC00L3QtSDQsNGC0LDQutGD0LXQvNGL0LUg0YHQu9C+0L3Riywg0LzQsNC70LXQvdGM0LrQuNC1LCDQvdC+INC00L7Qu9Cz0L7QttC40LLRg9GJ0LjQtSDQu9C10YLRg9GH0LjQtSDQvNGL0YjQuCDQuCDRgtCw0LrQsNGPINC20LUg0LXRhdC40LTQvdCwLiDQodC70L7QvdC+0LIg0LjRgdC60LvRjtGH0LDRgtGMINC90LUg0LHRg9C00LXQvCwg0YLQsNC6INC60LDQuiDQvtC90Lgg0L/RgNC+0YHRgtC+INCx0L7Qu9GM0YjQuNC1LCDQv9C+0Y3RgtC+0LzRgyDQnNCw0YXQsNC70LDQvdC+0LHQuNGBINC40YUg0LLRi9C00LXQu9GP0LXRgiwg0L7QtNC90LDQutC+INC+0L3QuCDRhdC+0YDQvtGI0L4g0YHQvtCz0LvQsNGB0YPRjtGC0YHRjyDRgSDQvNC+0LTQtdC70YzRjiAo0LLQuNC00L3QviDQv9C+INC60LDRgNGC0LjQvdC60LUsINC90LDQv9GA0LjQvNC10YApLiDQmNGB0LrQu9GO0YfQuNC8INCy0YvQsdGA0L7RgdGLOgoKYGBge3J9Cm1vZGVsLmJlc3Qub3V0IDwtIGxtKExJRkVTUEFOIH4gQlJBSU5fV0UgKyBQUkVEX0lORF8yICsgUFJFRF9JTkRfMywgZGF0YSA9IGRmLmxvZy5uYS5iZXN0W2MoLTYsIC0xNCwgLTMxKSxdKQptb2RlbC5iZXN0Lm91dC5iZXRhIDwtIGxtLmJldGEobW9kZWwuYmVzdC5vdXQpCnN1bW1hcnkobW9kZWwuYmVzdC5vdXQuYmV0YSkKYGBgCgrQodGD0LTRjyDQv9C+ICRSXjIkINC4INC10LPQviDQuNGB0L/RgNCw0LLQu9C10L3QvdC+0LzRgyDQstCw0YDQuNCw0L3RgtGDLCDQsCDRgtCw0LrQttC1INC/0L4gcC12YWx1ZSDQt9C90LDRh9C40LzQvtGB0YLQuCDRgNC10LPRgNC10YHRgdC40LgsINC/0L7Qu9GD0YfQuNC70Lgg0LHQvtC70LXQtSDRhdC+0YDQvtGI0YPRjiDQvNC+0LTQtdC70YwuCgojIyAyMi4g0J/RgNC10LTRgdC60LDQt9Cw0L3QuNGPCgrQn9GA0LXQtNGB0LrQsNC20LXQvCAqKtCy0YDQtdC80Y8g0LbQuNC30L3QuCoq0L7QsdGL0LrQvdC+0LLQtdC90L3QvtCz0L4g0LXQttCwLiAqKtCS0LXRgSDQtdCz0L4g0LzQvtC30LPQsCoqIC0tLSAkMy4zJCDQsywg0LjQvNC10LXRgiDQt9Cw0YnQuNGC0YMg0L7RgiDRhdC40YnQvdC40LrQvtCyLCDQvdC+INC60YDRg9C/0L3Ri9C1INGF0LjRidC90YvQtSDQv9GC0LjRhtGLINGD0YHQv9C10YjQvdC+INC40YUg0LDRgtCw0LrRg9GO0YIsINC/0L7RjdGC0L7QvNGDICoq0LjQvdC00LXQutGBINGF0LjRidC90LjRh9C10YHRgtCy0LAqKiAtLS0g0L/RgNC40LzQtdGA0L3QviAkMyQuINCSINC90LXQstC+0LvQtSDQtdC20Lgg0LbQuNCy0YPRgiAkNCQtJDYkINC70LXRgi4g0J/Rg9GB0YLRjCDQsdGD0LTQtdGCICQ1JDoKCmBgYHtyfQpkYXRhLmhlZGdlaG9nIDwtIGRhdGEuZnJhbWUoTkFNRSA9ICJFdXJvcGVhbiBoZWRnZWhvZyIsIEJSQUlOX1dFID0gbG9nKDMuMyksIExJRkVTUEFOID0gbG9nKDUpLCBQUkVEX0lORF8yID0gMCwgUFJFRF9JTkRfMyA9IDEpCmRhdGEuaGVkZ2Vob2cgfD4gcHJpbnRfZGYoKQpgYGAKCtCf0YDQtdC00YHQutCw0LbQtdC8INC10LPQviDQv9GA0L7QtNC+0LvQttC40YLQtdC70YzQvdC+0YHRgtGMINC20LjQt9C90Lg6CgpgYGB7cn0KcHJlZC5oZWRnZWhvZy5wcmVkIDwtIHByZWRpY3QobW9kZWwuYmVzdC5vdXQsIG5ld2RhdGEgPSBkYXRhLmhlZGdlaG9nLCBpbnRlcnZhbCA9ICJwcmVkaWN0IikKcHJlZC5oZWRnZWhvZy5jb25mIDwtIHByZWRpY3QobW9kZWwuYmVzdC5vdXQsIG5ld2RhdGEgPSBkYXRhLmhlZGdlaG9nLCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikKYGBgCgpgYGB7cn0KZXhwKHByZWQuaGVkZ2Vob2cucHJlZFssIDFdKQpgYGAKCtCf0L7Qu9GD0YfQuNC70Lgg0YbQuNGE0YDRiyDQsdC70LjQt9C60LjQtSDQuiDQtNC10LnRgdGC0LLQuNGC0LXQu9GM0L3QvtGB0YLQuC4g0J/QvtGB0LzQvtGC0YDQuNC8INC90LAg0L/RgNC10LTRgdC60LDQt9Cw0YLQtdC70YzQvdGL0Lkg0LjQvdGC0LXRgNCy0LDQuzoKCmBgYHtyfQpwcmludCgi0J3QuNC20L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLnByZWRbLCAyXSkKcHJpbnQoItCS0LXRgNGF0L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLnByZWRbLCAzXSkKYGBgCgrQldC20Lgg0LIg0L3QtdCy0L7Qu9C1INC80L7Qs9GD0YIg0LbQuNGC0Ywg0LTQviAxMCDQu9C10YIuCgrQkCDRgtC10L/QtdGA0Ywg0L3QsCDQtNC+0LLQtdGA0LjRgtC10LvRjNC90YvQuToKCmBgYHtyfQpwcmludCgi0J3QuNC20L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLmNvbmZbLCAyXSkKcHJpbnQoItCS0LXRgNGF0L3Rj9GPINCz0YDQsNC90LjRhtCwOiIpCmV4cChwcmVkLmhlZGdlaG9nLmNvbmZbLCAzXSkKYGBgCg==